Proof of Concept and Beyond with AWS AppSync
Bootstrapping ideas into reality

What is AppSync?
AppSync is Amazon’s fully managed GraphQL API service offering. Where a developer simply defines their model in the form of a GraphQL schema, and automatically has a deployed GraphQL API with prebuilt templates for resolving to a database, such as DynamoDB.
There are competitors to AppSync with other fully managed GraphQL services, such as Hasura. A full list of these offerings can be found on the official GraphQL implementation website.
The “Cherry Pick” Pattern
AppSync was unveiled at AWS re:Invent 2019 as a central piece to the “cherry pick” paradigm. Where user authentication and control is defined at the very top GraphQL layer, and the rest of your application can be built on top of AppSync with additional services. A simple starter example that follows a AWS Well Architected pattern:

With this starter example, you have an application base that is set up to cover a wide casting suite of needs. Out of the box you can make any common CRUD operations to a database, as well as implement complex logic in one or many AWS Lambda functions. All without writing a single line of code. Here is what the generated CDK stack looks like:
import * as cdk from '@aws-cdk/core';
import lambda = require('@aws-cdk/aws-lambda');
import {
CfnApiKey,
PrimaryKey,
Values,
GraphqlApi,
MappingTemplate,
FieldLogLevel,
Schema
} from '@aws-cdk/aws-appsync';
import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb';
export class TheSimpleGraphQLServiceStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
/**
* Create a new AppSync GraphQL API
*/
const api = new GraphqlApi(this, 'Api', {
name: `demoapi`,
logConfig: {
fieldLogLevel: FieldLogLevel.ALL
},
schema: Schema.fromAsset('schema/schema.graphql')
});
const apiKey = new CfnApiKey(this, 'the-simple-graphql-service-api-key', {
apiId: api.apiId
});
/**
* Create new DynamoDB Table for Customer
*/
const customerTable = new Table(this, 'CustomerTable', {
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: {
name: 'id',
type: AttributeType.STRING
}
});
/**
* Add Customer DynamoDB as a Datasource for the Graphql API.
*/
const customerDS = api.addDynamoDbDataSource('Customer', customerTable);
// Query Resolver to get all Customers
customerDS.createResolver({
typeName: 'Query',
fieldName: 'getCustomers',
requestMappingTemplate: MappingTemplate.dynamoDbScanTable(),
responseMappingTemplate: MappingTemplate.dynamoDbResultList()
});
// Query Resolver to get an individual Customer by their id
customerDS.createResolver({
typeName: 'Query',
fieldName: 'getCustomer',
requestMappingTemplate: MappingTemplate.dynamoDbGetItem('id', 'id'),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
});
// Mutation Resolver for adding a new Customer
customerDS.createResolver({
typeName: 'Mutation',
fieldName: 'addCustomer',
requestMappingTemplate: MappingTemplate.dynamoDbPutItem(
PrimaryKey.partition('id').auto(),
Values.projecting('customer')
),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
});
// Mutation Resolver for updating an exisiting Customer
customerDS.createResolver({
typeName: 'Mutation',
fieldName: 'saveCustomerWithFirstOrder',
requestMappingTemplate: MappingTemplate.dynamoDbPutItem(
PrimaryKey.partition('order').auto().sort('customer').is('customer.id'),
Values.projecting('order').attribute('referral').is('referral')
),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
});
// Mutation Resolver for deleting an exisiting Customer
customerDS.createResolver({
typeName: 'Mutation',
fieldName: 'removeCustomer',
requestMappingTemplate: MappingTemplate.dynamoDbDeleteItem('id', 'id'),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
});
// defines an AWS Lambda resource
const loyaltyLambda = new lambda.Function(this, 'LoyaltyLambdaHandler', {
runtime: lambda.Runtime.NODEJS_12_X, // execution environment
code: lambda.Code.fromAsset('lambda'), // code loaded from the "lambda" directory
handler: 'loyalty.handler' // file is "loyalty", function is "handler"
});
/**
* Add Loyalty Lambda as a Datasource for the Graphql API.
*/
const loyaltyDS = api.addLambdaDataSource('Loyalty', loyaltyLambda);
// Query Resolver to get all Customers
loyaltyDS.createResolver({
typeName: 'Query',
fieldName: 'getLoyaltyLevel',
requestMappingTemplate: MappingTemplate.lambdaRequest(),
responseMappingTemplate: MappingTemplate.lambdaResult()
});
// GraphQL API Endpoint
new cdk.CfnOutput(this, 'Endpoint', {
value: api.graphqlUrl
});
// API Key
new cdk.CfnOutput(this, 'API_Key', {
value: apiKey.attrApiKey
});
}
}
This CDK stack can be extended to fit the needs of almost any web application by creating your own schema.graphql file and using Amazon’s built in resolvers for mapping to DynamoDB
Given how easy is it to have a fully functioning API without writing a single line of code, you can imagine the endless possibilities from here. AWS outlines this in their re:Invent 2019 Cherry Talk pick by illustrating a hypothetical progression of feature development for an airline.

From left to right, you can see the iteration of how cherry picked development can scale from a simple loyalty program proof of concept, to a fully fledged booking system with various downstream AWS services such as Step Functions, RDS, Cognito, and Secrets Manager.
The promoted best practices for adding on the services generally follows these rules:
- Use Lambda for complex logic
- Use state machines for long transactions. Pipeline resolvers for simpler transactions
- Enforce authorization at API, data field and operation level
- Use purpose-built databases
Closing thoughts
I have had a great hands on experience leveraging this pattern for building POC’s that escalate from an innovation idea, to integrated systems used day to day. The AppSync service and its conventions are more than just a toy to play around with for building “Hello World” apps, but rather a dependable service for scaling a serious application.
For Liberty Mutual employees, see myConnections for a blog post detailing how I bootstrapped a project with this pattern.
If you would like to learn more about the cherry pick pattern or AppSync and general, I would highly recommend watching the AWS re:Invent 2019 keynote on the topic: