Rapid Development Techniques for APIs with GraphQL on AWS [Graph Meetup @ Milan]

Rapid Development Techniques for APIs with GraphQL on AWS [Graph Meetup @ Milan]

36ca8ae7e19067021d0e39c0b72acc2a?s=128

Alex Casalboni

May 07, 2019
Tweet

Transcript

  1. 1.

    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
  2. 2.

    About me • Software Engineer & Web Developer • Worked

    in a startup for 4.5 years • ServerlessDays Organizer • AWS Customer since 2013
  3. 6.

    Today’s mobile experience is lacking Mobile website, no native apps

    Slow to book, low conversion rates Backend built for high-speed desktops !
  4. 7.

    Your job is to define tomorrow’s experience Digital agency hired

    for design First prototype in 4 weeks Must use real data, no mockup !
  5. 8.

    Our Goal Enable backend services for prototype Allow design to

    evolve Keeping up with day-to-day job !
  6. 9.

    { "_id": 3, "name": "ElastiLodge South Zanetown", "image": "https://placehold.it/300x300.png", "description":

    ” ... ", "location": "South Zanetown", "manager": { "name": ”Marco Rossi", "email": ”marco.rossi@elastilodge.com" }, "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
  7. 11.

    /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", ... } ] }
  8. 13.

    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
  9. 15.

    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
  10. 16.

    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 } }
  11. 17.

    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
  12. 19.

    Welcome AWS AppSync! Managed & serverless Multiple data sources Data-sync,

    real-time, and offline Conflict detection in the cloud Enterprise security features
  13. 20.

    Data Sources REST Endpoint /graphql Amazon Cognito Amazon DynamoDB AWS

    Lambda Amazon Elasticsearch Amazon RDS AWS Lambda
  14. 21.

    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.
  15. 24.

    Hotel Detail: Design What data is required? Can we use

    existing data sources? How can we enable via AppSync? Challenges? Missing data?
  16. 25.

    Mapping to Legacy API { "_id": 3, "image": "https://placehold.it/300x300.png", "name":

    "EL Resort & Spa", "description": "...", "location": "Fiji", "manager": { "name": "Florentino Jacobi", "email": "florentino.jacobi@elastilodge.com" }, "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": [ ... ] }
  17. 26.

    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!) }
  18. 27.

    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
  19. 28.

    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)
  20. 29.

    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)") }
  21. 30.

    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 … }
  22. 33.

    Reservations: Design What data is required? Can we use existing

    data sources? How can we enable via AppSync? Challenges?
  23. 34.

    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!) }
  24. 35.

    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) } } }
  25. 38.

    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
  26. 40.

    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) } }
  27. 43.

    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?
  28. 44.

    How to evolve our GraphQL API GraphQL supports multiple data

    sources per query: 1. Add new field 2. Configure new Data Source and Resolvers
  29. 45.

    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" } } }
  30. 46.

    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 } }
  31. 49.

    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
  32. 50.

    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
  33. 51.

    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.
  34. 54.

    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