From REST to GraphQL

From REST to GraphQL

F34d97ba1bfea0ff5e35a9c198562402?s=128

Marc-Andre Giroux

September 06, 2016
Tweet

Transcript

  1. From REST to GraphQL Marc-Andre Giroux @__xuorig__

  2. About me

  3. None
  4. A simple UI component

  5. None
  6. What kind of data is needed?

  7. Cart Product ProductImage

  8. Reusable endpoints (REST)

  9. /carts/1 /products/1 /products/2 /products/3 /product_images/1 /product_images/2 /product_images/3

  10. Too many round trips!

  11. /carts/1?expand=products

  12. /carts/1?fields=products(name, description, price)

  13. /carts/1?fields=products/name,description,price

  14. Custom Endpoints

  15. /cart_with_all_the_stuff_i_need

  16. None
  17. /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

  18. None
  19. Server Client Updates a view Creates a new view Product

    view v2 Product model changes Update endpoints Create new endpoint
  20. GraphQL

  21. What GraphQL is NOT

  22. What GraphQL IS

  23. { myShop { name } } Field Selection Set

  24. { myShop { name } } Lexed Parsed Validated Executed

    { “myShop” { “name”: “GitHub” } }
  25. { myShop { name } }

  26. { myShop { name } }

  27. None
  28. { shop(id: 1) { name } }

  29. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  30. { “myShop”: { “name”: “Full Stack Fest Shop” “location” {

    “city”: “Barcelona” “address”: “Av. Diagonal 547” } “products”: [{ “name”: “Conference Ticket” “price”: 500000 }, { “name”: “Cool T-Shirt” “price”: 20000 }] } }
  31. Type System

  32. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  33. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  34. type QueryRoot { myShop: Shop shop(id: Int): Shop }

  35. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  36. type Shop { name: String location: Address products(orderby: OrderEnum): [Product]

    } enum ProductOrderEnum { PRICE, POPULARITY, ALPHABETICAL }
  37. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  38. type Address { city: String address: String }

  39. { myShop { name location { city address } products(orderby:

    POPULARITY) { name price } } }
  40. type Product { name: String price: Int }

  41. Fragments

  42. { myShop { name location { city address } products(orderby:

    POPULARITY) { id name price } } }
  43. None
  44. { myShop { name location { city address } products(orderby:

    POPULARITY) { id name price } } }
  45. fragment productFields on Product { id name price }

  46. { myShop { name location { city address } products(orderby:

    POPULARITY) { ...productFields } } }
  47. query Fragment Fragment Fragment Fragment

  48. Introspection

  49. query { __schema { … } }

  50. Static Validation Code Generation IDE Integration Auto Documentation

  51. None
  52. None
  53. Resolving fields

  54. type Product { name: String price: Int }

  55. ProductType = GraphQL::ObjectType.define do name "Product" description “A product sold

    at a shop” # … end
  56. field :name do type types.String resolve -> (obj, args, ctx)

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

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

    do obj.subtotal + obj.taxes + obj.shipping_price end end
  59. POST /graphql

  60. Mutations

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

    } }
  62. Drawbacks and solutions

  63. N+1 Queries

  64. field :image do type ImageType resolve -> (product, args, ctx)

    do product.image end end field :products do type [ProductType] resolve -> (shop, args, ctx) do shop.products end end
  65. 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" = …
  66. Solution: Batching + Caching

  67. field :image do type ImageType resolve -> (product, args, ctx)

    do RecordLoader.for(Image).load(product.image_id) end end
  68. HTTP Caching

  69. Solution: Client Side Cache

  70. Normalized Cache

  71. Normalized Cache

  72. query { shop { products { price } } }

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

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

  75. Security

  76. Query Depth Timeouts Query Complexity

  77. Future

  78. Subscriptions

  79. subscription { productInventorySubscribe { products { inventory } } }

  80. Deferred Queries

  81. query { shop { name description products { name price

    } } }
  82. query { shop { name description products { name price

    } } }
  83. query { shop { name description products @defer { name

    price } } }
  84. None
  85. None
  86. Thank you Marc-Andre Giroux @__xuorig__