Pro Yearly is on sale from $80 to $50! »

Designing and implementing GraphQL API

Designing and implementing GraphQL API

34be88398f623c109b61d23e8215bd23?s=128

Mariusz Gil

May 31, 2019
Tweet

Transcript

  1. DEsigning and implementing Mariusz Gil @mariuszgil GRAPHQL API

  2. None
  3. None
  4. None
  5. None
  6. None
  7. Modeling database theory

  8. Searching data with elastic search

  9. storing data with mYsql 8

  10. Now it is time for… accessing our data from app

  11. Let’s start in 2000

  12. None
  13. All is resource based ]

  14. All is resource based With extra constraints ]

  15. /tweets Resource / collection (or store) archetype

  16. /tweets/123 Resource / document archetype

  17. /tweets/123/media Resource / (embedded) collection archetype

  18. /tweets/123/media/123 Resource / document archetype, inside (embedded) collection

  19. /tweets/123/block Resource / controller archetype

  20. /tweets Resource GET PUT POST PATCH DELETE http method

  21. /tweets Resource GET PUT POST PATCH DELETE http method READ

    REPLACE CREATE MODIFY DELETE Action / crUD-style + =
  22. https://nordicapis.com/what-is-the-richardson-maturity-model/

  23. None
  24. Main REST API pros Easy to understand Easy to document

    Easy to use Easy to implement Easy to scale Resources are disconnected from representations
  25. Main REST API cons Communication ping-pong Network heavy Time consuming

    … Communication ping-pong Network heavy Time consuming
  26. Let’s talk about other options ]

  27. None
  28. Graphql is query language for api With server runtime for

    processing queries Using defined type system
  29. Type system ]

  30. type Tweet { id: ID! created_at: String! text: String! }

    type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! }
  31. type Tweet { id: ID! created_at: String! text: String! user:

    User } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! }
  32. type Tweet { id: ID! created_at: String! text: String! user:

    User } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! tweets: [Tweet] }
  33. type Tweet { id: ID! created_at: String! text: String! user:

    User } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! tweets: [Tweet] tweets_count: Int }
  34. type Tweet { id: ID! created_at: String! text: String! user:

    User } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! tweets: [Tweet] tweets_count: Int } type Retweet { id: ID! created_at: String! in_reply_to_tweet_id: String! in_reply_to_user_id: String! retweeted_status: Tweet! user: User }
  35. type Tweet { id: ID! created_at: String! text: String! user:

    User retweets: [Retweet] retweet_count: Int } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! tweets: [Tweet] tweets_count: Int } type Retweet { id: ID! created_at: String! in_reply_to_tweet_id: String! in_reply_to_user_id: String! retweeted_status: Tweet! user: User }
  36. type Tweet { id: ID! created_at: String! text: String! user:

    User retweets: [Retweet] retweet_count: Int } enum SearchResponse { mixed recent popular } type User { id: ID! screen_name: String! name: String! profile_image_url: String created_at: String! tweets: [Tweet] tweets_count: Int } type Retweet { id: ID! created_at: String! in_reply_to_tweet_id: String! in_reply_to_user_id: String! retweeted_status: Tweet! user: User }
  37. interface Tweet { id: ID! created_at: String! text: String! user:

    User retweets: [Retweet] retweet_count: Int }
  38. type TextTweet implements Tweet { id: ID! created_at: String! text:

    String! user: User retweets: [Retweet] retweet_count: Int } type MediaTweet implements Tweet { id: ID! created_at: String! text: String! user: User media: [TweetImage] retweets: [Retweet] retweet_count: Int } interface Tweet { id: ID! created_at: String! text: String! user: User retweets: [Retweet] retweet_count: Int }
  39. Communication ]

  40. req resp req resp req resp CLIENT SERVER REST

  41. WHAT YOU ASK IS WHAT YOU GET

  42. req resp CLIENT SERVER GRAPHQL

  43. req resp CLIENT SERVER Reads writes /graphql?q=…

  44. { twitter { tweet(id: "1134427651672875008") { text } } }

    { "data": { "twitter": { "tweet": { "text": "IT'S @daycamp4devs DAY! :) \\o/ \\o/ \\o/ \\o/ \\o/ https://t.co/RrTFHQKUNt" } } } } REQUEST RESPONSE
  45. { twitter { tweet(id: "1134427651672875008") { text, created_at } }

    } { "data": { "twitter": { "tweet": { "text": "IT'S @daycamp4devs DAY! :) \\o/ \\o/ \\o/ \\o/ \\o/ https://t.co/RrTFHQKUNt” "created_at": "Fri May 31 11:53:23 +0000 2019” } } } } REQUEST RESPONSE
  46. { twitter { tweet(id: "1134427651672875008") { text, created_at, user }

    } } WRONG REQUEST! REQUEST RESPONSE
  47. { twitter { tweet(id: "1134427651672875008") { text, created_at, user {

    screen_name } } } } { "data": { "twitter": { "tweet": { "text": "IT'S @daycamp4devs DAY! :) \\o/ \\o/ \\o/ \\o/ \\o/ https://t.co/RrTFHQKUNt”, "created_at": "Fri May 31 11:53:23 +0000 2019”, "user": { "screen_name": "CalEvans" } } } } } REQUEST RESPONSE
  48. Let see it in action Almost live ;)

  49. None
  50. Query execution

  51. { twitter { tweet(id: "1134427651672875008") { text, created_at } }

    } { "data": { "twitter": { "tweet": { "text": "IT'S @daycamp4devs DAY! :) \\o/ \\o/ \\o/ \\o/ \\o/ https://t.co/RrTFHQKUNt” "created_at": "Fri May 31 11:53:23 +0000 2019” } } } } REQUEST RESPONSE
  52. import { GraphQLSchema, GraphQLObjectType, GraphQLID, GraphQLString, GraphQLNonNull, GraphQLInt, GraphQLList, GraphQLScalarType,

    GraphQLEnumType } from "graphql"; DATA DEFINITION
  53. let TweetType = new GraphQLObjectType({ name : 'Tweet', description :

    'A tweet object', fields : () => ({ id : { type: GraphQLID }, created_at : { type: GraphQLString }, text : { type: GraphQLString }, retweet_count : { type: GraphQLInt }, }) }); DATA DEFINITION
  54. let twitterType = new GraphQLObjectType({ name : 'TwitterAPI', description :

    'The Twitter API', fields : { tweet: { type : TweetType, args : { id : { type : new GraphQLNonNull(GraphQLString), description : 'Unique ID of tweet' } } } } }); export const QueryObjectType = twitterType; DATA DEFINITION
  55. let twitterType = new GraphQLObjectType({ name : 'TwitterAPI', description :

    'The Twitter API', fields : { tweet: { type : TweetType, args : { id : { type : new GraphQLNonNull(GraphQLString), description : 'Unique ID of tweet' } }, // RESOLVER resolve: (_, { id: tweetId }) => YOUR DATA ACCESS CODE GOES HERE! } } }); export const QueryObjectType = twitterType DATA RESOLVER DEFINITION
  56. let twitterType = new GraphQLObjectType({ name : 'TwitterAPI', description :

    'The Twitter API', fields : { tweet: { type : TweetType, args : { id : { type : new GraphQLNonNull(GraphQLString), description : 'Unique ID of tweet' } }, // RESOLVER resolve: (_, { id: tweetId }) => twitter.getTweet(tweetId) } } }); export const QueryObjectType = twitterType; DATA RESOLVER DEFINITION
  57. let TweetType = new GraphQLObjectType({ name : 'Tweet', description :

    'A tweet object', fields : () => ({ id : { type: GraphQLID }, created_at : { type: GraphQLString }, text : { type: GraphQLString }, retweet_count : { type: GraphQLInt }, }) }); DATA DEFINITION
  58. let TweetType = new GraphQLObjectType({ name : 'Tweet', description :

    'A tweet object', fields : () => ({ id : { type: GraphQLID }, created_at : { type: GraphQLString }, text : { type: GraphQLString }, retweet_count : { type: GraphQLInt }, retweets : { type : new GraphQLList(RetweetType), description : 'Get a list of retweets', args : { limit: { type : GraphQLInt, defaultValue : 5 } }, // RESOLVER resolve: ({ id_str: tweetId }, { limit }) => YOUR DATA ACCESS CODE GOES HERE! } }) }); DATA RESOLVER DEFINITION
  59. { twitter { tweet(id: "1134427651672875008") { text, created_at, user {

    screen_name, tweets(limit: 2) { retweets(limit: 2) { user { name } } } } } } } { "data": { "twitter": { "tweet": { "text": "IT'S @daycamp4devs DAY! :) \\o/ \\o/ \\o/ \\o/ \\o/ https://t.co/RrTFHQKUNt”, "created_at": "Fri May 31 11:53:23 +0000 2019”, "user": { "screen_name": "CalEvans", "tweets": [ { "retweets": [ { "user": { "name": "Eric Hogue" } }, { „user": { "name": "Cristiano D. Silva" } } ] }, { "retweets": [] } ] } } } } } REQUEST RESPONSE
  60. Execution performance

  61. req resp CLIENT SERVER /graphql?q=… GRAPHQL

  62. req resp CLIENT SERVER GRAPHQL DATA STORAGE 1 REQUEST N+M

    QUERIES? /graphql?q=…
  63. Cache and batch your data, Even in request scope

  64. None
  65. var DataLoader = require('dataloader') var tweetLoader = new DataLoader(keys =>

    batchedTweetKeys(tweetKeys)); var userLoader = new DataLoader(keys => batchedUserKeys(usersKeys)); tweetLoader.load(tweetId) .then(user => userLoader.load(user.id) .then(retweeted_to => tweetLoader.load(retweeted_to.id) DATALOADER DEFINITION
  66. let TweetType = new GraphQLObjectType({ name : 'Tweet', description :

    'A tweet object', fields : () => ({ id : { type: GraphQLID }, created_at : { type: GraphQLString }, text : { type: GraphQLString }, retweet_count : { type: GraphQLInt }, retweets : { type : new GraphQLList(RetweetType), description : 'Get a list of retweets', args : { limit: { type : GraphQLInt, defaultValue : 5 } }, resolve: ({ id_str: tweetId }, { limit }) => dbTeetLoader.load([ 'SELECT tweetId FROM retweets WHERE originalTweetId=? LIMIT ?', tweetId, limit ]).then(rows => rows.map(row => tweetLoader.load(row.tweetId))) } }) }); DATALOADER DEFINITION
  67. Schema synchronization

  68. Other use cases? ]

  69. https://labs.getninjas.com.br/sharing-data-in-a-microservices-architecture-using-graphql

  70. Kubernetes API gateway GRAPHQL server on top of gloo and

    envoy
  71. $> sqoopctl install kube SAMPLE CONFIGURATION

  72. $> sqoopctl install kube $> kubectl apply -f PATH_TO_CONFIG/tweets.yaml SAMPLE

    CONFIGURATION
  73. $> sqoopctl install kube $> kubectl apply -f PATH_TO_CONFIG/tweets.yaml $>

    glooctl get upstream +--------------------------------+------------+----------+-------------+ | NAME | TYPE | STATUS | FUNCTION | +--------------------------------+------------+----------+-------------+ | gloo-tweets-8080 | kubernetes | Accepted | getTweet | +--------------------------------+------------+----------+-------------+ SAMPLE CONFIGURATION
  74. $> kubectl get upstreams -n gloo-tweets-8080 -o yaml apiVersion: gloo.solo.io/v1

    kind: Upstream metadata: // TRUNCATED spec: upstreamSpec: kube: selector: app: tweets serviceName: tweets serviceNamespace: gloo-system servicePort: 8080 serviceSpec: rest: transformations: getTweet: body: {} headers: :method: text: GET :path: text: /api/tweets/{{ default(id, "") }} content-length: text: "0" content-type: {} transfer-encoding: {} SAMPLE CONFIGURATION
  75. $> sqoopctl schemat create tweets -f tweets.graphql $> sqoopctl resolvermap

    register -u default-tweets-8080 -s tweets getTweet Query tweet SAMPLE CONFIGURATION
  76. Where to look for more? ]

  77. None
  78. None
  79. None
  80. None
  81. None
  82. thanks @mariuszgil