Slide 1

Slide 1 text

Alex Casalboni Technical Evangelist, AWS @alex_casalboni Rapid Development Techniques for APIs with GraphQL on AWS @ 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved bit.ly/graphql-aws-milano

Slide 2

Slide 2 text

About me • Software Engineer & Web Developer • Worked in a startup for 4.5 years • ServerlessDays Organizer • AWS Customer since 2013

Slide 3

Slide 3 text

This talk is for developers < frontend | backend >

Slide 4

Slide 4 text

How do you prototype an API? bit.ly/graphql-aws-milano

Slide 5

Slide 5 text

Congratulations! You’ve just been hired.

Slide 6

Slide 6 text

Today’s mobile experience is lacking Mobile website, no native apps Slow to book, low conversion rates Backend built for high-speed desktops !

Slide 7

Slide 7 text

Your job is to define tomorrow’s experience Digital agency hired for design First prototype in 4 weeks Must use real data, no mockup !

Slide 8

Slide 8 text

Our Goal Enable backend services for prototype Allow design to evolve Keeping up with day-to-day job !

Slide 9

Slide 9 text

{ "_id": 3, "name": "ElastiLodge South Zanetown", "image": "https://placehold.it/300x300.png", "description": ” ... ", "location": "South Zanetown", "manager": { "name": ”Marco Rossi", "email": ”[email protected]" }, "timeZone": "US/Central", "phoneNumber": "802-918-3262", "category": 5, "address": { "street": "410 Simonis Forest", "zip": "07545-2308", "city": "South Zanetown", "country": "Virgin Islands, British" }, "deskHours": "24 hours", "amenities": [ ... ], "restaurants": [ ... ] } Legacy REST API

Slide 10

Slide 10 text

REST API challenges bit.ly/graphql-aws-milano

Slide 11

Slide 11 text

/posts /comments /authors REST API Chatty & Over fetching { "posts" : [ { "id" : 1, "content" : "the quick brown fox jumps over the…", "author" : "/authors/123", "created" : 1550486509, "comments" : "/posts/1/comments", ... }, { "id" : 2, "content" : "the quick brown fox jumps over…", "author" : "/authors/123" "created" : 1550486509, "comments" : "/posts/2/comments", ... } ] }

Slide 12

Slide 12 text

Why GraphQL bit.ly/graphql-aws-milano

Slide 13

Slide 13 text

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. https://graphql.org

Slide 14

Slide 14 text

Why GraphQL? posts comments authors GraphQL API Type safe Contract-driven Efficient Easy to test Realtime

Slide 15

Slide 15 text

How does GraphQL work? type Query { getTodos: [Todo] } type Todo { id: ID! name: String description: String priority: Int duedate: String } Model data with application schema query { getTodos { id name priority } } Client requests what it needs { "id": "1", "name": "Get Milk", "priority": "1" }, { "id": "2", "name": "Go to gym", "priority": "5" },… Only that data is returned

Slide 16

Slide 16 text

GraphQL Operations Queries Read data Mutations Write data Subscriptions Listen for data query { search(q: “harry potter”) { title } } mutation { create(title: “new book”) { id } } subscription { onCreate { id title } }

Slide 17

Slide 17 text

Is REST dead then? When data drives UI Structured data Query-driven Client-driven development Use GraphQL When you leverage HTTP Caching, Content types Hypermedia For resources (e.g. Amazon S3) Use REST Pick the appropriate protocol for your use case

Slide 18

Slide 18 text

How to deploy a GraphQL API? bit.ly/graphql-aws-milano

Slide 19

Slide 19 text

Welcome AWS AppSync! Managed & serverless Multiple data sources Data-sync, real-time, and offline Conflict detection in the cloud Enterprise security features

Slide 20

Slide 20 text

Data Sources REST Endpoint /graphql Amazon Cognito Amazon DynamoDB AWS Lambda Amazon Elasticsearch Amazon RDS AWS Lambda

Slide 21

Slide 21 text

Resolvers Amazon DynamoDB /graphql Request Resolver Maps payload to underlying data source via Velocity Template to execute query / mutation. Response Resolver Maps response from data source to GraphQL response via Velocity Template.

Slide 22

Slide 22 text

KEEP CALM and WATCH the DEMO

Slide 23

Slide 23 text

Let’s prototype! Phase #1: Hotel Detail bit.ly/graphql-aws-milano

Slide 24

Slide 24 text

Hotel Detail: Design What data is required? Can we use existing data sources? How can we enable via AppSync? Challenges? Missing data?

Slide 25

Slide 25 text

Mapping to Legacy API { "_id": 3, "image": "https://placehold.it/300x300.png", "name": "EL Resort & Spa", "description": "...", "location": "Fiji", "manager": { "name": "Florentino Jacobi", "email": "[email protected]" }, "timeZone": "US/Central", "address": { "street": "123 Main St", ”postalcode": "07545", "city": "Momi Bay", "country": "Fiji" }, "phoneNumber": "1-888-123-4567", "category": 5, "deskHours": "24 hours", "amenities": [ ... ], "restaurants": [ ... ] }

Slide 26

Slide 26 text

Hotel Detail: GraphQL Schema type Hotel { hotelId: ID! name: String! image: String location: String! phoneNumber: AWSPhone! address: Address! ... } type Query { listHotels(offset: String, limit: Int) getHotel(hotelId: ID!) }

Slide 27

Slide 27 text

Hotel Detail: Resolvers ## Resolver: Request Mapping ## { "version": "2018-05-29", "method": "GET", "resourcePath": "/hotels/${ctx.args.hotelId}", "params": { "headers": { "Content-Type": "application/json" } }, } ## Resolver: Response Mapping ## #if($ctx.result.statusCode == 200) ## If response is 200, return the body. ## Could also filter result... $ctx.result.body #else ## If not 200, append response to error $utils.appendError($ctx.result.body) #end

Slide 28

Slide 28 text

Hotel Detail: Result query { getHotel(hotelId: 3) { name location image phoneNumber address { street city } amenities } } { "name": "ElastiLodge North Rodgerstad", "location": "North Rodgerstad", "image": "/300x300.png", "phoneNumber": "(214) 210-4674", "address": { "street": "123 Main St", "city": "North Rodgerstad" }, "amenities": [ ... ] } (from 8KB to 312B -> 93% lighter response)

Slide 29

Slide 29 text

Mobile Code – Initialize App Sync Client do { // create app sync configuration (cache, service config etc) let config = … // client initialization let appSyncClient = try AWSAppSyncClient(appSyncConfig: config) } catch { fatalError("Error initializing appsync client. \(error)") }

Slide 30

Slide 30 text

Mobile Code – Run a Query // call a GraphQL Query appSyncClient?.fetch(query: GetHotelQuery(hotelId: hotelId), cachePolicy: .fetchIgnoringCacheData) {(result, error) in guard let r = result, error == nil else { print(error?.localizedDescription ?? "") return } // use the data in the GUI self.hotelDetails = r.data?.getHotel … }

Slide 31

Slide 31 text

KEEP CALM and WATCH the DEMO

Slide 32

Slide 32 text

Let’s prototype! Phase #2: Reservations bit.ly/graphql-aws-milano

Slide 33

Slide 33 text

Reservations: Design What data is required? Can we use existing data sources? How can we enable via AppSync? Challenges?

Slide 34

Slide 34 text

Reservations: GraphQL Schema type Reservation { confirmationNumber: ID! hotel: Hotel! guest: User! startDate: AWSDate! endDate: AWSDate! rate: Int! } type Query { ... reservations(guestId : ID!):[Reservation] } type Mutation { createReservation(input:ReservationInput) deleteReservation(confNum: ID!) }

Slide 35

Slide 35 text

Reservations: Query Resolver - Input #set( $todayString = $util.time.nowISO8601().substring(0, 10) ) { "version": "2017-02-28", "operation": "Query", "query": { "expression": "guestId = :gId AND startDate > :startDate", "expressionValues": { ":gId": $util.dynamodb.toDynamoDBJson($ctx.identity.sub), ":startDate": $util.dynamodb.toDynamoDBJson($todayString) } } }

Slide 36

Slide 36 text

Reservations: Query Resolver - Output $util.toJson($ctx.result.items)

Slide 37

Slide 37 text

Reservations: Result query { guestReservations { confirmationNumber hotel { hotelId name image phoneNumber } startDate endDate } }

Slide 38

Slide 38 text

Extending Reservation Workflow Legacy System /graphql Subscription (MQTT) Mutate reservation record, modifying record in DynamoDB and triggering subscription DynamoDB Stream: Put event in queue for processing AWS Lambda AWS Lambda Amazon DynamoDB Amazon SNS Amazon SNS

Slide 39

Slide 39 text

Subscription Query subscription ReservationSubscription { createReservationEvent(guestId:1) { confirmationNumber } }

Slide 40

Slide 40 text

Mobile Code – Subscribe to changes let sub = CreateReservationEventSubscription(guestId: userId) newReservationSubscription = try appSyncClient?.subscribe(subscription: sub, resultHandler: { (result, transaction, error) in // check for error // store a reference to the new booking into our cache transaction?.update(query: GuestReservationsQuery(guestId: userId)) { (data: inout GuestReservationsQuery.Data) in data.guestReservations?.append(bookingToAdd) // consume the data in the UI self.bookingDetails?.append(bookingToAdd) } }

Slide 41

Slide 41 text

KEEP CALM and WATCH the DEMO

Slide 42

Slide 42 text

Let’s prototype! Phase #3: Adaptive rates bit.ly/graphql-aws-milano

Slide 43

Slide 43 text

Adaptive Rates: Design R&D is building an adaptive rate capability Delivered via Amazon SageMaker API How do we add today’s rate to our existing API?

Slide 44

Slide 44 text

How to evolve our GraphQL API GraphQL supports multiple data sources per query: 1. Add new field 2. Configure new Data Source and Resolvers

Slide 45

Slide 45 text

Adaptive Rates: GraphQL Types and Resolvers type Hotel { hotelId: ID! name: String! location: String! ... rate: Rate } type Rate { hotelId: ID! rate: Int! currency: String! date: AWSDate! } ## rate Field Resolver: Request Mapping ## { "version": "2018-05-29", "method": "GET", "resourcePath": "/rd/rates/${ctx.source.hotelId}", "params": { "headers": { "Content-Type": "application/json" } } }

Slide 46

Slide 46 text

Adaptive Rates: Result query { getHotel(hotelId: 3) { name location image phoneNumber address { street city } amenities rate { currency rate } } } { "name": "ElastiLodge North Rodgerstad", "location": "North Rodgerstad", "image": "https://placehold.it/300x300.png", "phoneNumber": "(214) 210-4674", "address": { "street": “...", "city": “..." }, "amenities": [ ... ], "rate": { "currency": "USD", "rate": 312 } }

Slide 47

Slide 47 text

KEEP CALM and WATCH the DEMO

Slide 48

Slide 48 text

What did we just do? bit.ly/graphql-aws-milano

Slide 49

Slide 49 text

What did we accomplish today? Built a rich, flexible, mobile-ready API Incorporated legacy, new, and prototype features in a single endpoint Evolved API as requirements changed No coding required! Well, there were a few Velocity Templates AWS Console includes starter / boilerplate templates

Slide 50

Slide 50 text

AWS Amplify CLI Toolchain for developers Enables you to prototype and build quickly Built-in GraphQL Transformer Converts schema to CloudFormation templates and builds cloud resources $ amplify api add # create GraphQL schema # for example: type Hotel @model @searchable { name: String! location: String! ... } github.com/aws-amplify/amplify-cli

Slide 51

Slide 51 text

Backend Dev: Hmm. So you’re saying this “GraphQL” will allow any web/mobile developer to arbitrarily query basically any field in any backend service, recursively, however they want, without any backend developers involved? Frontend Dev: Yeah, right? It’s amazing! […silence…] Backend Dev: Guards, seize this person.

Slide 52

Slide 52 text

Real-world pitfalls Cost of queries on the backend Authentication and authorization

Slide 53

Slide 53 text

Code presented today github.com/sebsto/reinvent2018-mob320

Slide 54

Slide 54 text

Alex Casalboni Technical Evangelist, AWS @alex_casalboni @ 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved Thank you! bit.ly/graphql-aws-milano