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

GraphQL on Rails

GraphQL on Rails

Marc-Andre Giroux

September 24, 2016
Tweet

More Decks by Marc-Andre Giroux

Other Decks in Programming

Transcript

  1. GraphQL on Rails
    Marc-Andre Giroux @__xuorig__

    View Slide

  2. @__xuorig__

    View Slide

  3. @__xuorig__
    therakiasite.blogspot.com

    View Slide

  4. @__xuorig__
    Montreal, Canada

    View Slide

  5. @__xuorig__

    View Slide

  6. @__xuorig__

    View Slide

  7. @__xuorig__
    A simple UI component

    View Slide

  8. @__xuorig__

    View Slide

  9. @__xuorig__
    What kind of data is needed?

    View Slide

  10. @__xuorig__
    Cart Product ProductImage

    View Slide

  11. @__xuorig__
    Reusable endpoints (REST)

    View Slide

  12. @__xuorig__
    /carts/1
    /products/1
    /products/2
    /products/3
    /product_images/1
    /product_images/2
    /product_images/3

    View Slide

  13. @__xuorig__
    Too many round trips!

    View Slide

  14. @__xuorig__
    /carts/1?expand=products

    View Slide

  15. @__xuorig__
    /carts/1?fields=products(name, description, price)

    View Slide

  16. @__xuorig__
    /carts/1?fields=products/name,description,price

    View Slide

  17. @__xuorig__
    Custom Endpoints

    View Slide

  18. @__xuorig__
    /cart_with_all_the_stuff_i_need

    View Slide

  19. @__xuorig__

    View Slide

  20. @__xuorig__
    /cart_with_all_the_stuff_i_need
    /cart_version_2_with_all_the_things
    /cart_with_products_and_images
    /cart_with_products_and_images_with_price_and_taxes
    my_tightly_coupled_custom_endpoint_including_only_the_things_i_need_bla_bla_bla_bla
    /cart_with_products_and_images_with_price_and_taxes_but_no_description
    /cart_with_products_and_images_with_price_and_taxes_but_no_description_v2

    View Slide

  21. @__xuorig__

    View Slide

  22. @__xuorig__
    Server
    Client
    Updates a view
    Creates a new view
    New view version
    Model changes
    Update endpoints
    Create new endpoint

    View Slide

  23. @__xuorig__
    yo, give me the resource with id = abc
    OK, here’s the resource with id = abc
    Client Server

    View Slide

  24. @__xuorig__
    yo, give me the resource v2 with id = abc
    OK, here’s the resource v2 with id = abc
    Client Server

    View Slide

  25. @__xuorig__
    yo, give me the resource v3 with id = abc
    OK, here’s the resource v3 with id = abc
    Client Server

    View Slide

  26. @__xuorig__
    GraphQL

    View Slide

  27. @__xuorig__
    What GraphQL is NOT

    View Slide

  28. @__xuorig__
    What GraphQL IS

    View Slide

  29. @__xuorig__
    {
    myShop {
    name
    }
    } Field
    Selection Set

    View Slide

  30. @__xuorig__
    {
    myShop {
    name
    }
    }
    Lexed
    Parsed
    Validated
    Executed
    {
    “myShop” {
    “name”: “GitHub”
    }
    }

    View Slide

  31. @__xuorig__
    {
    myShop {
    name
    }
    }

    View Slide

  32. @__xuorig__
    {
    myShop {
    name
    }
    }

    View Slide

  33. @__xuorig__

    View Slide

  34. @__xuorig__
    {
    shop(id: 1) {
    name
    }
    }

    View Slide

  35. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  36. @__xuorig__
    {
    “myShop”: {
    “name”: “Euruko 2016”
    “location” {
    “city”: “Sofia”
    “address”: “Av. Diagonal 547”
    }
    “products”: [{
    “name”: “Conference Ticket”
    “price”: 500000
    }, {
    “name”: “Cool T-Shirt”
    “price”: 20000
    }]
    }
    }

    View Slide

  37. @__xuorig__
    Type System

    View Slide

  38. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  39. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  40. @__xuorig__
    type QueryRoot {
    myShop: Shop
    shop(id: Int): Shop
    }

    View Slide

  41. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  42. @__xuorig__
    type Shop {
    name: String
    location: Address
    products(orderby: OrderEnum): [Product]
    }
    enum ProductOrderEnum {
    PRICE,
    POPULARITY,
    ALPHABETICAL
    }

    View Slide

  43. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  44. @__xuorig__
    type Address {
    city: String
    address: String
    }

    View Slide

  45. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    name
    price
    }
    }
    }

    View Slide

  46. @__xuorig__
    type Product {
    name: String
    price: Int
    }

    View Slide

  47. @__xuorig__
    {
    myShop {
    name
    yolo {
    swag
    }
    }
    }

    View Slide

  48. @__xuorig__
    {
    myShop {
    name
    yolo {
    swag
    }
    }
    }
    type Shop {
    name: String
    location: Address
    }

    View Slide

  49. @__xuorig__
    Introspection

    View Slide

  50. @__xuorig__
    query {
    __schema {

    }
    }

    View Slide

  51. @__xuorig__
    Static Validation
    Code Generation
    IDE Integration
    Auto Documentation

    View Slide

  52. @__xuorig__

    View Slide

  53. @__xuorig__

    View Slide

  54. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    id
    name
    price
    }
    }
    }

    View Slide

  55. @__xuorig__

    View Slide

  56. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    id
    name
    price
    }
    }
    }

    View Slide

  57. @__xuorig__
    Fragments

    View Slide

  58. @__xuorig__
    fragment productFields on Product {
    id
    name
    price
    }

    View Slide

  59. @__xuorig__
    {
    myShop {
    name
    location {
    city
    address
    }
    products(orderby: POPULARITY) {
    ...productFields
    }
    }
    }

    View Slide

  60. @__xuorig__
    query
    Fragment
    Fragment
    Fragment Fragment

    View Slide

  61. @__xuorig__
    Mutations

    View Slide

  62. @__xuorig__
    mutation {
    createProduct(name: “Nice Mug”, price: 10000) {
    id
    name
    }
    }

    View Slide

  63. @__xuorig__
    Resolving fields

    View Slide

  64. @__xuorig__
    github.com/rmosolgo/graphql-ruby

    View Slide

  65. @__xuorig__
    ProductType = GraphQL::ObjectType.define do
    name "Product"
    description “A product sold at a shop”
    # …
    end

    View Slide

  66. @__xuorig__
    field :name do
    type types.String
    resolve -> (obj, args, ctx) { obj.name }
    end

    View Slide

  67. @__xuorig__
    field :price do
    type types.Int
    resolve -> (obj, args, ctx) do
    obj.subtotal + obj.taxes + obj.shipping_price
    end
    end

    View Slide

  68. @__xuorig__
    ShopType = ObjectType.define do
    name “Shop"
    description "A Shop"
    field :products, [ProductType]
    end

    View Slide

  69. @__xuorig__
    QueryType = ObjectType.define do
    name “Query"
    description “The Root of my Schema"
    field :myShop, ShopType
    end

    View Slide

  70. @__xuorig__
    MySchema = GraphQL::Schema.new(
    query: QueryRoot,
    mutation: MutationRoot
    )

    View Slide

  71. @__xuorig__
    POST /graphql

    View Slide

  72. @__xuorig__
    module Api
    class GraphqlController < ApiController
    def query
    query_string = params[:query]
    variables = params[:variables]
    context = {
    current_user: current_user
    }
    # ...
    end
    end
    end

    View Slide

  73. @__xuorig__
    module Api
    class GraphqlController < ApiController
    def query
    # ...
    result = MySchema.query(
    query_string,
    variables: variables,
    context: context
    )
    render json: result
    end
    end
    end

    View Slide

  74. @__xuorig__
    Drawbacks
    and solutions

    View Slide

  75. @__xuorig__
    N+1 Queries

    View Slide

  76. @__xuorig__
    # Product Type
    field :image do
    type ImageType
    resolve -> (product, args, ctx) do
    product.image
    end
    end
    # Shop Type
    field :products do
    type [ProductType]
    resolve -> (shop, args, ctx) do
    shop.products
    end
    end

    View Slide

  77. @__xuorig__
    Product Load (1.0ms) SELECT "products".* FROM "products"
    WHERE "products"."shop_id" = …
    Image Load (0.9ms) SELECT "images".* FROM "images"
    WHERE "images"."product_id" = …
    Image Load (0.2ms) SELECT "images".* FROM "images"
    WHERE "images"."product_id" = …
    Image Load (0.1ms) SELECT "images".* FROM "images"
    WHERE "images"."product_id" = …

    View Slide

  78. @__xuorig__
    Solution: Batching + Caching

    View Slide

  79. @__xuorig__
    field :image do
    type ImageType
    resolve -> (product, args, ctx) do
    RecordLoader.for(Image).load(product.image_id)
    end
    end

    View Slide

  80. @__xuorig__
    HTTP Caching

    View Slide

  81. @__xuorig__
    Solution: Client Side Cache

    View Slide

  82. @__xuorig__
    Normalized Cache

    View Slide

  83. @__xuorig__
    query {
    shop {
    products {
    price
    }
    }
    }
    query {
    shop {
    product(id: 1) {
    price
    }
    }
    }

    View Slide

  84. @__xuorig__
    {
    root: {
    shop: {
    products: [
    Link.new(1)
    ]
    }
    },
    1: {
    price: 1000
    }
    }

    View Slide

  85. @__xuorig__
    https://github.com/facebook/relay http://www.apollostack.com/

    View Slide

  86. @__xuorig__
    Security

    View Slide

  87. @__xuorig__
    Query Depth
    Timeouts
    Query Complexity

    View Slide

  88. @__xuorig__
    Future

    View Slide

  89. @__xuorig__
    Subscriptions

    View Slide

  90. @__xuorig__
    subscription {
    productInventorySubscribe {
    products {
    inventory
    }
    }
    }

    View Slide

  91. @__xuorig__
    Deferred Queries

    View Slide

  92. @__xuorig__
    query {
    shop {
    name
    description
    products {
    name
    price
    }
    }
    }

    View Slide

  93. @__xuorig__
    query {
    shop {
    name
    description
    products {
    name
    price
    }
    }
    }

    View Slide

  94. @__xuorig__
    query {
    shop {
    name
    description
    products @defer {
    name
    price
    }
    }
    }

    View Slide

  95. @__xuorig__

    View Slide

  96. @__xuorig__
    yo, give me the resource with id = abc
    OK, here’s the resource with id = abc
    Client Server

    View Slide

  97. @__xuorig__
    Client Server
    yo, give me the resource with id = 1
    yo, give me the resource with id = 2
    yo, give me the resource with id = 3
    yo, give me the resource with id = 4

    View Slide

  98. @__xuorig__
    oh boy :(
    Client Server

    View Slide

  99. @__xuorig__
    I need that exact data shape (requirements)
    Here is everything you can do! (capabilities)
    Client Server

    View Slide

  100. @__xuorig__
    Efficiency

    View Slide

  101. @__xuorig__
    Predictability

    View Slide

  102. @__xuorig__
    Thank you!
    Marc-Andre Giroux @__xuorig__

    View Slide