Beginner-friendly mobile backend based on AWS AppSync

Andreas Wittig – 07 Mar 2019

Are you looking for a way to build a backend for a mobile or web application on AWS? You should check out the newcomer announced by AWS last year: AWS AppSync which provides a GraphQL based API endpoint. AppSync is serverless and very beginner friendly.

Beginner-friendly mobile backend based on AWS AppSync

What is GraphQL and why do we need it?

GraphQL is a mobile-first approach to build an API and is, therefore, an exciting alternative to RESTful APIs. Facebook is the main contributor to GraphQL. What are the main advantages of GraphQL?

  • Minimizing the data transfer: The query language allows the client to ask for exactly the data that is needed, nothing more and nothing less.
  • Reducing the number of requests: A request cannot only access a single resource but combine multiple queries.
  • Defining a schema: A schema is the core of each GraphQL API. The used type system makes sure your API is well-defined by default.
  • Evolving an API: Changing an API without the need of versioned APIs is possible due to the flexible schema and clients defining their expected responses.
  • Unifying access: A typical use case for a GraphQL is to unify the access to different backend systems (e.g., legacy applications, microservices, …).

A side note, GraphQL has nothing to do with a graph database. With GraphQL you define your API and might use a SQL, NoSQL, or even more specialized database behind.

Next, we will have a look at GraphQL as a Service on AWS: AppSync.

Sample Application with AWS AppSync

AppSync is very easy to use. All you need to do to create a Serverless API is:

  1. Create a GraphQL schema.
  2. Define the data sources: DynamoDB, Lambda, Elasticsearch, generic HTTP, …
  3. Configure a mapping between your GraphQL schema and the data sources: resolvers.

The repository widdix/aws-cutting-edge-appsync contains a sample application based on AppSync, DynamoDB, and Lambda. The sample application Favorite AWS Architecture lets you vote for the services you would include in your favorite AWS architecture.

The following figure shows the architecture of the Favorite AWS Architecture sample application which is typical for a web application based on AppSync:

  • AppSync: the GraphQL API endpoint.
  • S3 Bucket: stores and delivers the static assets of the single page application.
  • Lambda function: executes your business logic.
  • DynamoDB: the NoSQL database.

Typical architecture based on AppSync

Defining a Schema

A GraphQL schema consists of the following parts:

  • Queries define the read-only operations.
  • Mutations specify the write operations.
  • Subscriptions configure long-lived connections for receiving real-time data.

A GraphQL schema is comparable to an Open API specification (formerly known as Swagger).

We define the following queries, mutations, and subscriptions for the Favorite AWS Architecture sample application.

type Query {
getVotingResults(nextToken: String): VotingResults
getServices(nextToken: String): Services
}

type Mutation {
vote(service: ServiceType!): Boolean
}

type Subscription {
voted: Boolean
@aws_subscribe(mutations: ["vote"])
}

On top of that, The GraphQL schema contains the type definition.

The following snippet shows the type definition for our Favorite AWS Architecture sample application.

enum ServiceType {
ec2
lambda
fargate
clb
nlb
alb
appsync
apigateway
eks
ecs
rds_aurora
rds_postgres
rds_mysql
rds_mariadb
dynamodb
s3
efs
ebs
}

type Services {
items: [Service]
nextToken: String
}

type Service {
type: ServiceType
name: String
}

type VotingResults {
items: [VotingResult]
nextToken: String
}

type VotingResult {
service: ServiceType!
upvotes: Int!
}

AppSync supports the GraphQL scalar types ID, String, Int, Float, and Boolean. On top of that, AppSync defines additional scalar types like AWSDate, AWSEmail, and AWSURL. See Scalar types in AWS AppSync for more details.

Even interfaces and unions are possible with GraphQL. See Interfaces and Unions in GraphQL for more details.

Supported data sources

AppSync supports the following data sources out of the box:

  • A DynamoDB table.
  • A Elasticsearch domain.
  • A Lambda function.
  • A RDS Aurora database cluster.
  • Any HTTP endpoint.

Our sample application makes use of two data sources: DynamoDB and Lambda. As usual, we are following an infrastructure as code approach based on CloudFormation. The following snippet shows how to define a DynamoDB table, a Lambda function, and the AppSync data sources.

VoteTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: service
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: service
KeyType: HASH
VoteDataSource:
Type: 'AWS::AppSync::DataSource'
Properties:
Type: AMAZON_DYNAMODB
ServiceRoleArn: !GetAtt 'VoteRole.Arn'
ApiId: !GetAtt 'GraphQLApi.ApiId'
Name: vote
DynamoDBConfig:
TableName: !Ref VoteTable
AwsRegion: !Ref 'AWS::Region'
ServicesFunction:
Type: 'AWS::Lambda::Function'
Properties:
Handler: 'index.handler'
Runtime: 'nodejs8.10'
MemorySize: 128
Timeout: 30
Role: !GetAtt 'ServicesFunctionRole.Arn'
Code:
ZipFile: |
'use strict';
exports.handler = async (event, context) => {
return {
items: [
{type: "ec2", name: "ec2"},
// ...
]};
};
ServicesDataSource:
Type: 'AWS::AppSync::DataSource'
Properties:
Type: AWS_LAMBDA
ServiceRoleArn: !GetAtt 'ServicesRole.Arn'
ApiId: !GetAtt 'GraphQLApi.ApiId'
Name: service
LambdaConfig:
LambdaFunctionArn: !GetAtt ServicesFunction.Arn

Next, we need to connect the data sources to the GraphQL schema. To do so, we need to configure resolvers.

Resolvers

Besides connecting a data source to a query or mutation, a resolver allows you to configure mapping templates for the data sources’ request and response. The template engine Velocity is used by AppSync.

So, first of all, you need to write a request mapping template which transforms the incoming request to a format that the downstream data source can process.

In our sample application Favorite AWS Architecture the following request mapping template is used to query all data from the DynamoDB table storing the voting results from getVotingResults(nextToken: String): VotingResults.

{
"version": "2017-02-28",
"operation": "Scan",
"nextToken": #if( $context.args.nextToken ) "$context.args.nextToken" #else null #end
}

The response mapping template is even simpler. It just forwards the result from DynamoDB to the client in JSON format.

$util.toJson($context.result)

Again, we are using CloudFormation to create the resolver itself.

VotingResultsResolver:
Type: 'AWS::AppSync::Resolver'
Properties:
TypeName: Query
DataSourceName: !GetAtt 'VoteDataSource.Name'
RequestMappingTemplateS3Location: './getVotingResults-request.vtl'
ResponseMappingTemplateS3Location: './getVotingResults-response.vtl'
ApiId: !GetAtt 'GraphQLApi.ApiId'
FieldName: getVotingResults

AppSync allows you to chain resolvers into a so-called pipeline as well. See Pipeline Resolvers for more details.

Subscriptions and offline access

GraphQL is built for mobile applications. Network connectivity is limited on mobile devices. Therefore, GraphQL frameworks like Apollo support caching and offline data access by default.

On top of that, AppSync offers subscriptions - implemented with MQTT over WebSockets - which allows you to notify the client about changes on the API. Necessary to know, with AppSync subscriptions are invoked as a response to a mutation. There is no other way to send a message to the client.

When to use subscriptions instead of polling or manually re-fetching data?

  • Initial data set is significant, but only small increments change over time.
  • Low-latency updates of the client are essential.

We are using subscriptions to update the voting results for all clients after each change for our Favorite AWS Architecture sample application.

The following snippet shows the subscription voted defined on the mutation vote.

type Mutation {
vote(service: ServiceType!): Boolean
}

type Subscription {
voted: Boolean
@aws_subscribe(mutations: ["vote"])
}

Deployment Pipeline

It is very convenient to deploy AppSync with CloudFormation. I built my first deployment pipeline for the Favorite AWS Architecture sample application based on CloudFormation, CodeBuild, and CodePipeline within two hours.

Summary

That’s it. AppSync is an easy and powerful way to build Serverless APIs on AWS. My favorite AWS architectures right now:

  • AppSync with Lambda and DynamoDB.
  • ALB with Fargate and RDS Aurora.

Check out Ten Tips And Tricks for Improving Your GraphQL API with AWS AppSync from re:Invent 2018 for practical tips as well.


This blog post is based on our talk Cutting-Edge Architectures Based on AppSync, Lambda, and Fargate. More than 500 engineers joined us for this talk at the AWS Summit in Berlin. Want us to inspire your developers and architects as well? Invite us to a session at your location.

Andreas Wittig

Andreas Wittig

I’ve been building on AWS since 2012 together with my brother Michael. We are sharing our insights into all things AWS on cloudonaut and have written the book AWS in Action. Besides that, we’re currently working on bucketAV,HyperEnv for GitHub Actions, and marbot.

Here are the contact options for feedback and questions.