Skip to main content

Working with Scully

This doc will guide you through the process of integrating a GraphQL API into your Scully projects.

Setting up an Angular project

This approach 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 project. We will then populate a page with information about each product.

NOTE

If you want to follow along and haven't created your own Scully project, you can check out our Scully starter repo here. This repo contains all the information you need to build and run the example in this doc.

Unlike most SSG's, Scully integrates well 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 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

To connect to your project, you need your API Endpoint and an API key.

If you haven't created a project yet, follow this guide to do so.

If you already have a project, your API Endpoint will be in the admin UI. In the home tab, look for the "Useful Snippets" section.

Copy the API Endpoint and save it somewhere secure.

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

Installing dotenv

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 GraphQL API service

To add the GraphQL API 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 ProductsService {

constructor(private http: HttpClient) { }

}

We need a method that can get our list of products. Define a method in the ProductsService class called getProducts. 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 ProductsService {

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{
price
name
_id
}
}
}`},
{headers}
);
}
}

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, 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 project's GraphQL API Endpoint. 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.

The final file should look like this:

products.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';

export interface Product {
name: string;
_id: string;
price: number;
}

@Injectable({
providedIn: 'root'
})
export class ProductsService {

constructor(private http: HttpClient) { }

getProductList() {
const headers= new HttpHeaders();
.set('content-type', 'application/json');
.set('Authorization', `Bearer YOUR-API-KEY-HERE`);

return this.http.post<any>('YOUR-ENDPOINT-HERE',
{"query":`query{
getProductList{
items{
price
name
_id
}
}
}`},
{headers});
}
}

That's it for adding your project's GraphQL API 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, open the products.service.ts file, and add the following code above your class:

products.service.ts
export interface Product {
name: string;
_id: 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 ProductsService 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, ProductsService } 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 productsService 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 productsService: ProductsService) {

}

ngOnInit(): void {
this.productsService.getProductList().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, ProductsService } from '../products.service';

@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
public products:Product[] = [];

constructor(
private productsService: ProductsService) {

}

ngOnInit(): void {
this.productsService.getProductList().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 the GraphQL API, 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.

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

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.name}}</h3>
<p>Price: {{product.price}}</p>
</div>
</div>

The final file should look like this:

<main>
<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>
</main>

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

ng build
npx scully
npx scully serve

The final page should look like this:

Still need help? Get in touch with us.