Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Designing and implementing GraphQL API

Designing and implementing GraphQL API

Mariusz Gil

May 31, 2019
Tweet

More Decks by Mariusz Gil

Other Decks in Programming

Transcript

  1. DEsigning and implementing
    Mariusz Gil
    @mariuszgil
    GRAPHQL API

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. Modeling database theory

    View Slide

  8. Searching data with elastic search

    View Slide

  9. storing data with mYsql 8

    View Slide

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

    View Slide

  11. Let’s start in 2000

    View Slide

  12. View Slide

  13. All is resource based
    ]

    View Slide

  14. All is resource based
    With extra constraints
    ]

    View Slide

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

    View Slide

  16. /tweets/123
    Resource / document archetype

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. /tweets
    Resource
    GET
    PUT
    POST
    PATCH
    DELETE
    http method
    READ
    REPLACE
    CREATE
    MODIFY
    DELETE
    Action / crUD-style
    + =

    View Slide

  22. https://nordicapis.com/what-is-the-richardson-maturity-model/

    View Slide

  23. View Slide

  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

    View Slide

  25. Main REST API cons
    Communication ping-pong
    Network heavy
    Time consuming

    Communication ping-pong
    Network heavy
    Time consuming

    View Slide

  26. Let’s talk about other options
    ]

    View Slide

  27. View Slide

  28. Graphql is query language for api
    With server runtime for processing queries
    Using defined type system

    View Slide

  29. Type system
    ]

    View Slide

  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!
    }

    View Slide

  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!
    }

    View Slide

  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]
    }

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  37. interface Tweet {
    id: ID!
    created_at: String!
    text: String!
    user: User
    retweets: [Retweet]
    retweet_count: Int
    }

    View Slide

  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
    }

    View Slide

  39. Communication
    ]

    View Slide

  40. req
    resp
    req
    resp
    req
    resp
    CLIENT SERVER
    REST

    View Slide

  41. WHAT YOU ASK IS
    WHAT YOU GET

    View Slide

  42. req
    resp
    CLIENT SERVER
    GRAPHQL

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  48. Let see it in action
    Almost live ;)

    View Slide

  49. View Slide

  50. Query execution

    View Slide

  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

    View Slide

  52. import {
    GraphQLSchema,
    GraphQLObjectType,
    GraphQLID,
    GraphQLString,
    GraphQLNonNull,
    GraphQLInt,
    GraphQLList,
    GraphQLScalarType,
    GraphQLEnumType
    } from "graphql";
    DATA DEFINITION

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  60. Execution performance

    View Slide

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

    View Slide

  62. req
    resp
    CLIENT SERVER
    GRAPHQL
    DATA
    STORAGE
    1 REQUEST
    N+M
    QUERIES?
    /graphql?q=…

    View Slide

  63. Cache and batch your data,
    Even in request scope

    View Slide

  64. View Slide

  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

    View Slide

  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

    View Slide

  67. Schema synchronization

    View Slide

  68. Other use cases?
    ]

    View Slide

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

    View Slide

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

    View Slide

  71. $> sqoopctl install kube
    SAMPLE CONFIGURATION

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  75. $> sqoopctl schemat create tweets -f tweets.graphql
    $> sqoopctl resolvermap register -u default-tweets-8080 -s tweets getTweet Query tweet
    SAMPLE CONFIGURATION

    View Slide

  76. Where to look for more?
    ]

    View Slide

  77. View Slide

  78. View Slide

  79. View Slide

  80. View Slide

  81. View Slide

  82. thanks
    @mariuszgil

    View Slide