How to create a highly scalable serverless GraphQL data-driven app in minutes

Gerard Sans
Jul 1 · 8 min read

Serverless made easy with AWS Amplify and Angular

In this tutorial, we will learn how to build a GraphQL data-driven serverless app using Angular, AWS Amplify and AWS AppSync. We will create a new project with Angular, generate a serverless GraphQL API using AWS AppSync and build an AWS AppSync client to access it including real-time. We will cover:

In order to follow this post you will need a basic knowledge of GraphQL. You can learn the basics following this tutorial at graphql.org.

Please let me know if you have any questions or want to learn more at @gerardsans.

Introduction to AWS AppSync

AWS AppSync allows you to create highly scalable serverless GraphQL data-driven mobile and web apps by providing an open source Amplify Framework (consisting of AWS AppSync Clients, GraphQL transforms and the CLI), integrations with multiple data sources, and the AWS AppSync console.

  • AWS AppSync Clients: client-side libraries to securely access data from any spec-compliant GraphQL API. Built-in support for real-time and offline/delta client synchronisation.
  • Amplify CLI: set of commands, via theapi category, to automate the setup and provision of resources for AWS AppSync and Amazon API Gateway cloud services. Support for REST and GraphQL APIs.
  • GraphQL Transforms: custom GraphQL schema directives that can be used in your GraphQL schema to enable custom workflows.
  • Data sources: databases (Amazon DynamoDB for NoSQL, Amazon Aurora for RDBMS), searches (Amazon Elasticsearch Service), and AWS Lambda functions.

By using AWS AppSync, teams can quickly create highly scalable serverless GraphQL data-driven apps for mobile and web while leaving the heavy lifting to the Amplify CLI.

AWS AppSync data-driven apps architecture overview

Setting up a new project with the Angular CLI

Before moving to the next section, please complete the steps described in “Setting up a new project with the Angular CLI”.

Creating a new GraphQL API

For this post we are going to create a GraphQL API to list our favourite restaurants. To create it, we will use the following command:

amplify add api

Answer the following questions

  • Please select from one of the below mentioned services GraphQL
  • Provide API name: RestaurantAPI
  • Choose an authorization type for the API API key
  • Do you have an annotated GraphQL schema? No
  • Do you want a guided schema creation? Yes
  • What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description)
  • Do you want to edit the schema now? Yes

When prompted, replace the default schema with the following:

type Restaurant @model {
id: ID!
clientId: String
name: String!
description: String!
city: String!
}

You should be familiar with everything in the GraphQL schema above but for the @model directive. This is a GraphQL transform provided by AppSync.

Using your first GraphQL transform

GraphQL transforms allow AppSync to provide further customisation and support for common scenarios, so you don’t have to.

By adding@model to theRestauranttype we are telling the Amplify CLI runtime to create the resolvers to support queries, mutations and subscriptions in DynamoDB. Besides the regular CRUD operations, we are also getting some more advanced features like pagination and filtering that we are going to cover later.

Once we apply@model to a type we get access to these other transforms:

  • @key to configure custom index structures in DynamoDB
  • @searchable to enable searches using Amazon Elasticsearch Service
  • @connection to add relationships between types
  • @lambda to generate AWS Lambda resolvers
  • @auth to add fine grained multi-authorisation support
  • @versioned to add conflict resolution for offline scenarios

Learn how to use each transform in detail from the official docs.

GraphQL Transforms are implemented using custom GraphQL schema directives as defined in the GraphQL specification.

Pushing your GraphQL API to the cloud

Let’s run the push command to create the GraphQL API and see the results of using the @model transform:

amplify push
  • Are you sure you want to continue? Yes
  • Do you want to generate code for your newly created GraphQL API Yes
  • Choose the code generation language target typescript
  • Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.ts
  • Do you want to generate/update all possible GraphQL operations — queries, mutations and subscriptions Yes
  • Enter maximum statement depth [increase from default if your schema is deeply nested] 2
  • Enter the file name for the generated code src/API.ts

Write down your GraphQL endpoint and API KEY.

Run the command below to access the AWS AppSync console.

amplify console api
  • Please select from one of the below mentioned services GraphQL

Testing your new GraphQL API

Once in the AWS AppSync console, click on Queries on the left side. This will open an editor that we can use to edit and test GraphQL queries.

Execute the following mutation to create a new restaurant:

mutation createRestaurant {
createRestaurant(input: {
name: "Nobu"
description: "Great Sushi"
city: "New York"
}) {
id name description city
}
}

Now, run this query to list the restaurant we just added:

query listRestaurants {
listRestaurants {
items {
id
name
description
city
}
}
}

Besides creating all common CRUD operations for your types, AWS AppSync also creates extra utility features like filters. Run this query to try using filters:

query searchRestaurants {
listRestaurants(filter: {
city: {
contains: "New York"
}
}) {
items {
id
name
description
city
}
}
}

AWS AppSync creates filters on GraphQL scalar types (ID, String, Int, Float, Boolean) for list operations like listRestaurants.

Building the AppSync client

We are going to use the Amplify GraphQL client for Angular. Nonetheless, there are two AppSync clients available:

  • AWS AppSync SDK, integrated with the Apollo client, supports multiple authorization models, real-time, offline and caching.
  • Amplify GraphQL client, lighter option if you don’t require offline or caching capabilities.

Library packages are also available for Mobile (iOS, Android, React Native) and Web (JavaScript, React, Angular and Vue).

Querying data with queries

In the earlier sections, we made sure we had a working GraphQL API. On the client, we will start by displaying a list.

import { API, graphqlOperation } from 'aws-amplify';
import { listRestaurants } from '../graphql/queries';
import { Restaurant } from './types/restaurant';
@Component({
template: `
<div>
<div *ngFor="let restaurant of restaurants">
{{ restaurant.name }}
</div>
</div>`
})
export class AppComponent implements OnInit {
restaurants: Array<Restaurant>;
async ngOnInit() {
var response = await API.graphql(graphqlOperation(listRestaurants))
this.restaurants = (response as any).data.listRestaurants.items;
}
}

From the code above, we are using API.graphqlto run the listRestaurantsquery within ngOnInit. Queries and types were created by the Amplify CLI during the push command. This call returns a promise that we handle by using async/await and render the result using ngFor.

You might be wondering why we used listRestaurants.items. This is because AWS AppSync created an intermediate type ModelRestaurantConnection. This is so we can handle pagination scenarios together withlimit and nextTokenparameters.

listRestaurants(filter: ModelRestaurantFilterInput, limit: Int, nextToken: String): ModelRestaurantConnectiontype ModelRestaurantConnection {
items: [Restaurant]
nextToken: String
}

Creating data with mutations

In order to add new restaurants, we are going to create a form using FormBuilder to take the required user input and pass it forward to the createRestaurant mutation.

import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { createRestaurant } from '../../graphql/mutations'
@Component(...)
export class HomeComponent implements OnInit {
public createForm: FormGroup;
constructor(private fb: FormBuilder) { }async ngOnInit() {
this.createForm = this.fb.group({
'name': ['', Validators.required],
'description': ['', Validators.required],
'city': ['', Validators.required]
});
var response = await API.graphql(graphqlOperation(listRestaurants));
this.restaurants = (response as any).data.listRestaurants.items;
}

public async onCreate(restaurant: any) {
try {
await API.graphql(graphqlOperation(createRestaurant, {
input: restaurant
}));

console.log('item created!');
this.restaurants = [restaurant, ...this.restaurants];
this.createForm.reset();
}
catch (e) {
console.log('error creating restaurant...', e);
}
}
}

Note how AWS AppSync instead of passing each field as an argument created an input type CreateRestaurantInput which makes our client code simpler and convenient.

type Mutation {
createRestaurant(input: CreateRestaurantInput!): Restaurant
}
input CreateRestaurantInput {
id: ID
clientId: String
name: String!
description: String!
city: String!
}

As we did before with queries, we are using async/await to run our mutation as we submit the form via onCreate. Once we get the result, we use the form data to update the list and clear the form inputs with reset.

Adding real-time with subscriptions

To demonstrate real-time, we are going to use a subscription to update AppSync clients when new restaurants are added. AppSync clients will subscribe to listen for changes going through our GraphQL API. More specifically, restaurant creation mutations.

import * as Observable from 'zen-observable';
import { onCreateRestaurant } from '../../graphql/subscriptions';
@Component(...)
export class HomeComponent implements OnInit {
ngOnInit() {
var subscription = API.graphql(
graphqlOperation(onCreateRestaurant)
) as Observable<object>;


subscription.subscribe({
next: (sourceData) => {
const newRestaurant = (sourceData as any).value.data.onCreateRestaurant
this.restaurants = [newRestaurant, ...this.restaurants];
}
});
}
}

GraphQL Amplify Client uses zen-observable, a light-weight Observable implementation.

We use the HomeComponent to create the subscription during ngOnInit and immediately subscribe to start listening for notifications. Then for each notification, we will update our list of restaurants with the new one.

Publishing your app via the AWS Amplify Console

The first thing you need to do is create a new repo for this project. Once you’ve created the repo, copy the URL for the project to the clipboard and initialise git in your local project:

git initgit remote add origin repo@repoofyourchoice.com:username/project-name.gitgit add .git commit -m 'initial commit'git push origin master

Next visit the AWS Amplify Console in your AWS account. Click Get Started to create a new deployment. Next, authorise your repository provider as the repository service. Next, choose the new repository and branch for the project you just created and click Next. In the next screen, create a new role and use this role to allow the AWS Amplify Console to deploy these resources and click Next. Finally, click Save and Deploy to deploy your application!

AWS Amplify Console deployment steps.

Cleaning up cloud services

If at any time, you would like to delete a service from your project and your AWS Account, you can do this by running:

amplify remove authamplify push

Conclusion

Congratulations! You successfully built your first GraphQL API using Angular and AWS AppSync. Thanks for following this tutorial.


Thanks for reading!

Have you got any questions about this tutorial or AWS AppSync? Feel free to reach me anytime at @gerardsans.

My Name is Gerard Sans. I am a Developer Advocate at AWS Mobile working with AWS AppSync and AWS Amplify teams.

GraphQL is an open source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.

Angular is an open source project from Google.

Gerard Sans

Written by

Developer Advocate @AWSCloud | Just be AWSome | MC Speaker Trainer Community Leader | Views are my own | @fullstackcon @ReactiveConf @ngcruise @UphillConf UK ☂