More Related Content Similar to Simpler by Design: Build a Better GraphQL API for Your Next App by Writing Less Code (MOB301) - AWS re:Invent 2018 (20) More from Amazon Web Services (20) Simpler by Design: Build a Better GraphQL API for Your Next App by Writing Less Code (MOB301) - AWS re:Invent 20182. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Simpler By Design:
Build A Better GraphQL API For Your
Next App By Writing Less Code
Michael Paris
SDE
AWS
M O B 3 0 1
3. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
What do modern web & mobile apps need?
What is AWS AppSync?
What is AWS Amplify?
How does it fit together and help you build applications?
Agenda
4. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A few things every app needs
Development
User management & Authorization
Real-time APIs
Client SDKs and tooling
Deployment
Multiple environments
Hosting
CI / CD
Analytics
5. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
How do we get there?
User management & Authorization
Amazon Cognito User Pools
Real-time APIs
AWS AppSync
Client SDKs and tooling
Amplify Framework
Multiple environments
Amplify CLI
Hosting & CI / CD
AWS Amplify Console
Analytics
Amazon Pinpoint
6. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A managed GraphQL gateway.
Define the shape of your API using GraphQL schema definition language (SDL)
Attach data sources that reference other AWS resources
Write resolvers that fetch data from data sources and attach them to fields
A real-time data broker.
Subscribe to any mutation out of the box
Reliable message delivery via MQTT over WebSockets
What is AWS AppSync?
7. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A Basic AWS AppSync API
AWS
Customer Account
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
type Post {
id: ID!
title: String
createdAt: String
updatedAt: String
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post Table
ARN
IAM Role
ARN
8. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
“GraphQL is a query language for APIs and a runtime for fulfilling those
queries with your existing data. GraphQL provides a complete and
understandable description of the data in your API, gives clients the
power to ask for exactly what they need and nothing more, makes it
easier to evolve APIs over time, and enables powerful developer tools.”
graphql.org
What is GraphQL?
9. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A query language for APIs…
Queries read data
Mutations write data
Subscriptions are pushed data in real-time
query GetPost {
getPost(id: ”1”) {
id
title
}
}
mutation CreatePost {
createPost(title: “GraphQL @re:invent”) {
id
title
}
} subscription OnCreatePost {
onCreatePost {
id
title
}
}
10. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A runtime for fulfilling those queries with your
existing data
11. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A complete and understandable description of the
data in your API…
schema {
query: Query
mutation: Mutation
}
type Query {
# Get a post by id.
getPost(id: ID!): Post
# Paginate through posts
listPosts(limit: Int, nextToken: String): PostConnection
}
12. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Gives clients the power to ask for exactly what they
need and nothing more
query GetPostAndComments {
getPost(id: “1”) {
id
title
comments {
id
content
}
}
}
{
“data”: {
“getPost”:
“id”: “1”,
”title”: “GraphQL @re:invent”,
“comments”: [
{
“id”: “2”,
“content”: “Love it!”
}
]
}
}
}
13. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Makes it easier to evolve APIs over time …
type Post {
# The editor of the post.
editor: User @deprecated(reason: “Replacing with multiple editors”)
# A set of editors for the post.
editors: [User]
}
14. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
And enables powerful developer tools…
15. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
A framework that provides an integrated developer
experience for web & mobile apps using AWS
Categories include api, auth, storage, analytics, and more
First class support for React, Angular, Ionic, React Native, iOS, Android
Opinionated solutions to problems that every app developer faces
A CLI that coordinates the moving parts
Deploy full application backends in a few key strokes
Supports multiple environments for multi user team collaboration
What is AWS Amplify?
16. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
AWS Amplify
$ amplify init
$ amplify add auth
$ amplify add storage
$ amplify add api
$ amplify push
Initialize an Amplify project
Add an Amazon Cognito User Pool
Create and secure an Amazon Simple
Storage Service (Amazon S3) bucket
Add an AWS AppSync API
Deploy via AWS CloudFormation
17. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Rapidly build scalable, data-driven applications
Declaratively define your app’s data model using GraphQL SDL and have it
transformed into a fully descriptive AWS CloudFormation stack
Powered by GraphQL directives
@model, @auth, @connection, @versioned, and @searchable
The GraphQL Transform
18. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Let’s look at a few examples
19. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
The @model transformer
# schema.graphql
type Post @model {
id: ID!
title: String!
}
$ amplify add api
20. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
$ amplify push
21. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
And voila!
AWS
Customer Account
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
type Post {
id: ID!
title: String
createdAt: String
updatedAt: String
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post Table
ARN
IAM Role
ARN
22. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Your API is ready to go.
Let’s query it.
23. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Create an object
mutation CreatePost {
createPost(input: {
title: “A new post!”
}) {
id
title
}
}
PutItem
{
“data”: {
“createPost”: {
”id”: “1”,
“title”: “A new post!”
}
}
}
24. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Get an object
query GetPost {
getPost(
id: ”1”
) {
id
title
}
}
GetItem
{
“data”: {
“getPost”: {
”id”: “1”,
“title”: “A new post!”
}
}
}
25. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
List and paginate objects
query ListPosts {
listPosts(limit: 10, nextToken: “...”) {
items {
id
title
}
nextToken
}
}
Scan/Query
{
“data”: {
“listPosts”: {
“items”: [{
”id”: “1”,
“title”: “A new post!”
}],
“nextToken”: “...”
}
}
}
26. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
{
“data”: {
“updatePost”: {
”id”: “1”,
“title”: “A different post!”
}
}
}
Update an object
mutation UpdatePost {
updatePost(input: {
id: “1”,
title: “A different post!”
}) {
id
title
}
}
DynamoDB UpdateItem
27. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Delete an object
mutation DeletePost {
deletePost(input: {
id: “1”
}) {
id
title
}
}
DynamoDB DeleteItem
{
“data”: {
“deletePost”: {
”id”: “1”,
“title”: “A different post!”
}
}
}
28. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@auth (static group)
type Post @model @auth(rules: [
{ allow: groups, groups: ["Admin"] }
]) {
id: ID!
title: String!
}
Admin
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post
Table
ARN
IAM Role
29. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@auth (dynamic group)
type Post @model @auth(rules: [{
allow: groups,
groupsField: “allowedGroups”
}]) {
id: ID!
title: String!
allowedGroups: [String!]!
}
allowedGroups field, mutations will fail and
queries will return null.
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post
Table
ARN
IAM Role
30. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@auth (ownership)
type Post @model @auth(rules: [
{ allow: owner, ownerField: “editors” }
]) {
id: ID!
title: String!
editors: [String!]!
}
editors field, mutations will fail and
queries will return null.
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post
Table
ARN
IAM Role
31. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@auth (combo)
type Post @model @auth(rules: [
{ allow: groups, groups: [“Admin”] },
{ allow: owner, ownerField: “editors” }
]) {
id: ID!
title: String!
editors: [String!]!
}
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post
Table
ARN
IAM Role
32. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@connection
type Post @model {
id: ID!
title: String!
comments: [Comment!]!
@connection(name: “PostComments”)
}
type Comment @model {
id: ID!
content: String!
post: Post
@connection(name: “PostComments”)
}
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
createComment(...): Comment
updateComment(...): Comment
deleteComment(...): Comment
}
type Query {
getPost(...): Post
listPosts(...): Post
getComment(...): Comment
listComment(...): Comment
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
onCreateComment(...): Comment
onUpdateComment(...): Comment
onDeleteComment(...): Comment
}
Schema Resolvers DataSources
AWS AppSync
Mutation
Query
Post
DataSource
Post
Table
ARN
IAM Role
Comment
Table
Comment
DataSource
Post
Table
ARN
IAM Role
createComment
updateComment
deleteComment
deletePost
updatePost
createPost
getPost
listPosts
getComment
listComments
Post
comments
Comment
post
gsi-PostComments
33. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Querying with @connection
query GetPostAndComments {
getPost(id: ”1”) {
id
title
comments(limit: 10, nextToken: “...”) {
items {
id
content
}
nextToken
}
}
}
gsi-PostComments
34. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@versioned
type Post @model @versioned {
id: ID!
title: String!
}
type Post {
id: ID!
title: String!
version: Int!
}
input UpdatePostInput {
id: ID!
title: String
expectedVersion: Int!
}
# Also added to DeletePostInput
35. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Querying with @versioned
mutation CreatePost {
createPost(input: {
title: “A new post!”
}) {
id
title
version
}
}
“data”
“createPost” {
“id”: “1”,
“title”: “A new post!”,
“version”: 1
}
36. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Querying with @versioned
mutation UpdatePost {
updatePost(input: {
id: “1”,
title: “An updated post!”,
expectedVersion: 1
}) {
id
title
version
}
}
“data”
“updatePost” {
“id”: “1”,
“title”: “An updated post!”,
“version”: 2
}
37. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Querying with @versioned
38. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Querying with @versioned
mutation UpdatePost {
updatePost(input: {
id: “1”,
title: “I prefer this title!”,
expectedVersion: 1
}) {
id
title
version
}
}
ConditionalUpdateFailedException
conflict detection
39. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
@searchable
type Post
@model
@searchable
{
id: ID!
title: String!
}
AWS Cloud
Amazon
DynamoDB
Post Table
type Mutation {
createPost(...): Post
updatePost(...): Post
deletePost(...): Post
}
type Query {
getPost(...): Post
listPosts(...): Post
}
type Subscription {
onCreatePost(...): Post
onUpdatePost(...): Post
onDeletePost(...): Post
}
Schema Resolvers DataSources
AWS AppSync
createPost
updatePost
deletePost
getPost
listPosts
Mutation
Query
Post Table
DataSource
Post
Table
ARN
IAM Role
AWS
Lambda
Streaming
Lambda
Amazon
Elasticsearch
Service
Amazon
ElasticSsarch
Service
searchPosts
ElasticSearch
DataSource
ES
Domain
ARN
IAM Role
40. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Search objects
query SearchPosts {
searchPosts(filter: {
title: {
match: “new”
}
}) {
items {
id
title
}
}
}
{
“data”: {
“searchPosts”: {
“items”: [{
”id”: “1”,
“title”: “A new post!”
}]
}
}
}
41. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Simple, declarative configuration.
Design your data model & let the tool do the work.
Works seamlessly with the Amplify Framework.
Instant offline & real-time support in client apps.
All data is stored in your AWS Account.
Use all the other great tools & services AWS has to offer.
Amplify API Overview
42. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
The Amplify Library
43. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
The Amplify Library
Easily connect AWS services to web and mobile apps.
Categories include API, Analytics, Auth, Function, Storage, etc
Simple to use abstractions for common use cases.
Support for many frameworks & platforms.
React, React-Native, iOS, Android, Ionic, Angular, etc.
44. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Configure Amplify
import Amplify from 'aws-amplify’;
import awsconfig from './aws-exports’;
Amplify.configure(awsconfig);
45. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Add the <Authenticator /> component
import React, { Component } from 'react';
import { Authenticator } from 'aws-amplify-react';
class Home extends Component {
render() {
return (
<div className="container home">
...
<Authenticator
onStateChange={this.handleAuthStateChange}
amplifyConfig={awsExports}
errorMessage={authErrorMessageMapper}
/>
...
</div>
);
}
}
46. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Provide data with <Connect />
<Connect
query={gql(queries.GetConvo, { id: this.props.conversation.id })}
subscription={gql(subscriptions.OnCreateMessage, { conversationId: this.props.conversation.id})}
onSubscriptionMsg={(prev, { onCreateMessage }) => {
addToEnd(prev.getConvo.messages.items, onCreateMessage)
return prev;
}}
>
{({ data, loading, error }) => { // Uses render props to provide data to child components
if (error) return (<h3>Error: {error}</h3>);
if (loading) return (<h3>Loading...</h3>);
const messages = getIn(data, “getConvo.messages.items”);
return (
<div>
{
messages.map((message, i) => (
<ChatMessage key={i} message={message} isFromMe={message.authorId === me.username} />
))
}
</div>
)}}
</Connect>
47. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Call mutations with API
import * as mutations from './graphql/mutations';
import { graphqlOperation, Analytics, API } from 'aws-amplify’;
async function createMessage(message) {
const response = await API.graphql(
graphqlOperation(mutations.CreateMessage, { input: message })
);
return response.data.createMessage;
}
48. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Instrument analytics
import React, { Component } from 'react';
import { Analytics } from 'aws-amplify’;
class App extends Component {
componentDidMount() {
Analytics.startSession();
window.addEventListener('beforeunload', () => {
Analytics.stopSession();
})
}
...
}
49. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Amplify Codegen
$ amplify add codegen
$ amplify codegen
# Generate queries & native types from your GraphQL API
# for iOS, Android, TypeScript, and Flow
50. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Multi environment support
$ git branch prod
$ git push origin prod
# Map git branches to Amplify environments
# Amplify console make CI / CD & hosting a breeze
51. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Demo
52. Thank you!
© 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.
Michael Paris
parismic@amazon.com
@mikeparisstuff
53. © 2018, Amazon Web Services, Inc. or its affiliates. All rights reserved.