GraphQL on Rails

GraphQL on Rails

F34d97ba1bfea0ff5e35a9c198562402?s=128

Marc-Andre Giroux

September 24, 2016
Tweet

Transcript

  1. GraphQL on Rails Marc-Andre Giroux @__xuorig__

  2. @__xuorig__

  3. @__xuorig__ therakiasite.blogspot.com

  4. @__xuorig__ Montreal, Canada

  5. @__xuorig__

  6. @__xuorig__

  7. @__xuorig__ A simple UI component

  8. @__xuorig__

  9. @__xuorig__ What kind of data is needed?

  10. @__xuorig__ Cart Product ProductImage

  11. @__xuorig__ Reusable endpoints (REST)

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

  13. @__xuorig__ Too many round trips!

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

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

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

  17. @__xuorig__ Custom Endpoints

  18. @__xuorig__ /cart_with_all_the_stuff_i_need

  19. @__xuorig__

  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

  21. @__xuorig__

  22. @__xuorig__ Server Client Updates a view Creates a new view

    New view version Model changes Update endpoints Create new endpoint
  23. @__xuorig__ yo, give me the resource with id = abc

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

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

    abc OK, here’s the resource v3 with id = abc Client Server
  26. @__xuorig__ GraphQL

  27. @__xuorig__ What GraphQL is NOT

  28. @__xuorig__ What GraphQL IS

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

  30. @__xuorig__ { myShop { name } } Lexed Parsed Validated

    Executed { “myShop” { “name”: “GitHub” } }
  31. @__xuorig__ { myShop { name } }

  32. @__xuorig__ { myShop { name } }

  33. @__xuorig__

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

  35. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { name price } } }
  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 }] } }
  37. @__xuorig__ Type System

  38. @__xuorig__ { myShop { name location { city address }

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

    products(orderby: POPULARITY) { name price } } }
  40. @__xuorig__ type QueryRoot { myShop: Shop shop(id: Int): Shop }

  41. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { name price } } }
  42. @__xuorig__ type Shop { name: String location: Address products(orderby: OrderEnum):

    [Product] } enum ProductOrderEnum { PRICE, POPULARITY, ALPHABETICAL }
  43. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { name price } } }
  44. @__xuorig__ type Address { city: String address: String }

  45. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { name price } } }
  46. @__xuorig__ type Product { name: String price: Int }

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

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

    } type Shop { name: String location: Address }
  49. @__xuorig__ Introspection

  50. @__xuorig__ query { __schema { … } }

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

  52. @__xuorig__

  53. @__xuorig__

  54. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { id name price } } }
  55. @__xuorig__

  56. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { id name price } } }
  57. @__xuorig__ Fragments

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

  59. @__xuorig__ { myShop { name location { city address }

    products(orderby: POPULARITY) { ...productFields } } }
  60. @__xuorig__ query Fragment Fragment Fragment Fragment

  61. @__xuorig__ Mutations

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

    name } }
  63. @__xuorig__ Resolving fields

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

  65. @__xuorig__ ProductType = GraphQL::ObjectType.define do name "Product" description “A product

    sold at a shop” # … end
  66. @__xuorig__ field :name do type types.String resolve -> (obj, args,

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

    ctx) do obj.subtotal + obj.taxes + obj.shipping_price end end
  68. @__xuorig__ ShopType = ObjectType.define do name “Shop" description "A Shop"

    field :products, [ProductType] end
  69. @__xuorig__ QueryType = ObjectType.define do name “Query" description “The Root

    of my Schema" field :myShop, ShopType end
  70. @__xuorig__ MySchema = GraphQL::Schema.new( query: QueryRoot, mutation: MutationRoot )

  71. @__xuorig__ POST /graphql

  72. @__xuorig__ module Api class GraphqlController < ApiController def query query_string

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

    ... result = MySchema.query( query_string, variables: variables, context: context ) render json: result end end end
  74. @__xuorig__ Drawbacks and solutions

  75. @__xuorig__ N+1 Queries

  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
  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" = …
  78. @__xuorig__ Solution: Batching + Caching

  79. @__xuorig__ field :image do type ImageType resolve -> (product, args,

    ctx) do RecordLoader.for(Image).load(product.image_id) end end
  80. @__xuorig__ HTTP Caching

  81. @__xuorig__ Solution: Client Side Cache

  82. @__xuorig__ Normalized Cache

  83. @__xuorig__ query { shop { products { price } }

    } query { shop { product(id: 1) { price } } }
  84. @__xuorig__ { root: { shop: { products: [ Link.new(1) ]

    } }, 1: { price: 1000 } }
  85. @__xuorig__ https://github.com/facebook/relay http://www.apollostack.com/

  86. @__xuorig__ Security

  87. @__xuorig__ Query Depth Timeouts Query Complexity

  88. @__xuorig__ Future

  89. @__xuorig__ Subscriptions

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

    }
  91. @__xuorig__ Deferred Queries

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

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

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

    name price } } }
  95. @__xuorig__

  96. @__xuorig__ yo, give me the resource with id = abc

    OK, here’s the resource with id = abc Client Server
  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
  98. @__xuorig__ oh boy :( Client Server

  99. @__xuorig__ I need that exact data shape (requirements) Here is

    everything you can do! (capabilities) Client Server
  100. @__xuorig__ Efficiency

  101. @__xuorig__ Predictability

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