Skip to main content

Use TakeShape with Scully

TakeShape is a perfect solution for integrating multiple API's into your Scully static site without bloating your project with excessive code.

This doc will guide you through the process of integrating TakeShape's API Mesh into your Scully projects.

Setting up an Angular project#

TakeShape can help you consolidate many different kinds of data for many different use-cases! But in this case we're going to pull a list of products from an ecommerce backend with an example TakeShape project. We will then populate a page with information about each product.

Unlike most SSG's, Scully integrates extremely easily with the Angular CLI's built-in functionality, so you don't need to add it to your Angular project from the beginning.

That being the case, to get started with a project, let's install the Angular CLI. Open the console in any directory and enter this:

npm install -g @angular/cli

To create a new project, you should navigate to the folder where you keep most of your projects. Next, use the Angular CLI to create a new app:

ng new your-app-name

Now that your app has been created, note that by default it's configured to use TypeScript. This documentation will proceed with instructions assuming you're using TypeScript, but all the information is valid if you're using vanilla JavaScript as well.

If you want to quickly test that your project is working fine, navigate to your project's directory and enter ng serve --open into the console.

Angular will serve your project on a localhost port without building it. By adding --open, your browser will automatically open to the homepage of your project.

Adding Scully to your project#

If your Angular project is already set up, you're ready to add Scully into the mix. First, you'll add scully to your project through Angular's CLI.

ng add @scullyio/init

A Scully config file will be created at the root of your project. You can read more about this file in the Scully docs, but we won't need to mess with it.

Next, you'll run a simple build command, and then execute the scully script with npx.

ng build
npx scully

Next, just use the npx scully serve command to serve your build, and you're set up! You will see in the console that that your project is being served on two separate ports: One for the Angular build, and one for Scully!

See the Scully docs for more info if something goes wrong.

Connecting a project#

If you already have an Angular project with Scully installed, there are a few steps to take before you can add TakeShape to the build process.

First you'll need a TakeShape project API Key and API Endpoint. If you haven't created a TakeShape project yet, you can do that by following our docs on creating new projects.

Otherwise, you'll find the API Endpoint in your project on the TakeShape web client. In the home tab, look in the bottom right of your screen for a section called Useful Snippets.

Copy the API Endpoint. You'll need this to connect your Scully project to TakeShape.

To get an API Key, you'll have to create one. Read our docs on creating an API Key here.

Installing dotenv#

In order to safely access your API Endpoint and API Key, we recommend using environment variables. Run npm install dotenv in your command line.

Next, create a file in the root directory of your project called .env. This will be a text file where you store your endpoint and key. Open this file up and add the following text to it:

.env
API_ENDPOINT=paste-your-endpoint-here
API_KEY=paste-your-key-here

Now you're all set to get started!

Implementing a TakeShape service#

To add TakeShape to your build process, you'll first need to create a Service in your Angular project that can fetch your data for you.

In the root of your project, open up the console. Enter ng g service products.

Open the products.service.ts file that will be generated in your app directory. Replace the contents with an empty class like this:

products.service.ts
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private http: HttpClient) { }
}

We need a method that can get our list of products. Define a method in the ProductService class called getProducts. In order to execute an http request from our service, we need to import several Http classes from angular. Follow along with the code below:

products.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';
require('dotenv').config()
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private http: HttpClient) { }
getProducts() {
const headers = new HttpHeaders()
.set('content-type', 'application/json')
.set('Authorization', `Bearer ${process.env.API_KEY}`)
return this.http.post<any>(`${process.env.API_ENDPOINT}`,
{"query":"query { getProductList{ items { name price } } }"},
{headers}
);
}
}
info

If you aren't using TypeScript, you don't need to add <any> before the argument list of the post method. It would instead look like this:

return this.http.post( ... )

As you can see in the above code, our getProducts method creates HttpHeaders with an Authorization property that we add a Bearer Token to. Your token will be your API Key, imported from your environment variables thanks to dotenv. To configure dotenv in this service, you simply place require('dotenv').configure() near the top of your file, as shown in the above example.

The getProducts method ultimately returns the result of a POST request made to your TakeShape API Endpoint. In order to query your API Mesh, you must add the "query" property to the body of your request, then define that property's value as the query you wish to run.

Our example query in the above code requests a full list of all products in our store, with the title, id and body contents.

That's it for adding TakeShape to the build process! Now we'll learn about using the data that you pull from your API Mesh.

Creating your Angular component#

Continuing with the above section's example of a project that imports products from a store, we'll return to our products.service.ts file. We'll need to add an interface to the service, because our project needs to know the shape of the data we'll be receiving from our API Mesh. This is a feature of TypeScript. To learn more, read the TypeScript docs.

For our purposes, simply open the products.service.ts file, and add the following code above your class:

products.service.ts
export interface Product {
name: string;
price: number;
}
info

If you aren't using TypeScript, you don't need to create an interface.

With that done, we'll create a component to pass our data to. At the root of your project, enter ng g component products in the console.

A folder called products will be created, with the html, css and javascript files necessary to build your component inside. In the products.component.ts file, you need to import both the ProductService class and Product interface. You then need the Component and OnInit classes from Angular.

products.component.ts
import { Component, OnInit } from '@angular/core';
import { Product, ProductService } from '../products.service';

Now you need a javascript decorator to define the properties of your component. Read the angular docs for more information.

products.component.ts
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})

Finally, we'll define our component class. We have to use Angular's ngOnInit method to call the getProducts method we defined in our productService class. This will then give access to the data retrieved with getProducts to your component.

products.component.ts
export class ProductsComponent implements OnInit {
public products:Product[] = [];
constructor(private productService: ProductService) {}
ngOnInit(): void {
this.productService.getProducts().subscribe(
(response) => {
this.products = response.data.getProductList.items
},
(error) => {
console.log(error);
}
);
}
}

The final file should look like this:

products.component.ts
import { Component, OnInit } from '@angular/core';
import { Product, ProductService } from '../products.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
public product:Product[] = [];
constructor(private productService: ProductService) {}
ngOnInit(): void {
this.productService.getProducts().subscribe(
(response) => {
this.products = response.data.getProductList.items
},
(error) => {
console.log(error);
}
);
}
}

Creating the template#

The previous section created a class that fetches products from TakeShape, then sets an array of the items to the class's products array. This array will be available in our component template to be displayed on the rendered page.

To access the data in your template, go to your products.component.html file. You can structure this file any way you want. Ours will be simple for this example.

<p>
This page is a list of info about all the products in our store.
</p>
<h3>
Products:
</h3>
<router-outlet></router-outlet>

After the <h3> in our above code, we'll insert a div for each product we retrieved in our getProducts method.

<div>
<div *ngFor="let product of products">
<h3>{{product.title}}</h3>
<p>Price: {{product._id}}</p>
</div>
</div>

The final file should look like this:

<p>
This page is a list of info about all the products in our store.
</p>
<h3>
Products:
</h3>
<div>
<div *ngFor="let product of products">
<h3>{{product.name}}</h3>
<p>Price: {{product.price}}</p>
</div>
</div>
<router-outlet></router-outlet>

Now to build and serve your project, enter these commands into the console:

ng build
npx scully
npx scully serve

And that's it! You're ready to show your TakeShape data to the world!

Still confused? Ask for help!. We'd love to solve your problem.