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 full-size slide

  2. @__xuorig__
    therakiasite.blogspot.com

    View full-size slide

  3. @__xuorig__
    Montreal, Canada

    View full-size slide

  4. @__xuorig__
    A simple UI component

    View full-size slide

  5. @__xuorig__
    What kind of data is needed?

    View full-size slide

  6. @__xuorig__
    Cart Product ProductImage

    View full-size slide

  7. @__xuorig__
    Reusable endpoints (REST)

    View full-size slide

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

    View full-size slide

  9. @__xuorig__
    Too many round trips!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  13. @__xuorig__
    Custom Endpoints

    View full-size slide

  14. @__xuorig__
    /cart_with_all_the_stuff_i_need

    View full-size slide

  15. @__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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. @__xuorig__
    GraphQL

    View full-size slide

  21. @__xuorig__
    What GraphQL is NOT

    View full-size slide

  22. @__xuorig__
    What GraphQL IS

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  25. @__xuorig__
    {
    myShop {
    name
    }
    }

    View full-size slide

  26. @__xuorig__
    {
    myShop {
    name
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. @__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 full-size slide

  30. @__xuorig__
    Type System

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  42. @__xuorig__
    Introspection

    View full-size slide

  43. @__xuorig__
    query {
    __schema {

    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  47. @__xuorig__
    Fragments

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  50. @__xuorig__
    query
    Fragment
    Fragment
    Fragment Fragment

    View full-size slide

  51. @__xuorig__
    Mutations

    View full-size slide

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

    View full-size slide

  53. @__xuorig__
    Resolving fields

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. @__xuorig__
    POST /graphql

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  64. @__xuorig__
    Drawbacks
    and solutions

    View full-size slide

  65. @__xuorig__
    N+1 Queries

    View full-size slide

  66. @__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 full-size slide

  67. @__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 full-size slide

  68. @__xuorig__
    Solution: Batching + Caching

    View full-size slide

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

    View full-size slide

  70. @__xuorig__
    HTTP Caching

    View full-size slide

  71. @__xuorig__
    Solution: Client Side Cache

    View full-size slide

  72. @__xuorig__
    Normalized Cache

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  76. @__xuorig__
    Security

    View full-size slide

  77. @__xuorig__
    Query Depth
    Timeouts
    Query Complexity

    View full-size slide

  78. @__xuorig__
    Future

    View full-size slide

  79. @__xuorig__
    Subscriptions

    View full-size slide

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

    View full-size slide

  81. @__xuorig__
    Deferred Queries

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  86. @__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 full-size slide

  87. @__xuorig__
    oh boy :(
    Client Server

    View full-size slide

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

    View full-size slide

  89. @__xuorig__
    Efficiency

    View full-size slide

  90. @__xuorig__
    Predictability

    View full-size slide

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

    View full-size slide