Using and Optimising GraphQl with Rails

4b6cdb0615742a6971f6d39537ec9010?s=47 Ankita
October 26, 2018

Using and Optimising GraphQl with Rails

Starting out with REST-ful APIs is a natural step for most client-facing applications. As business requirements evolve continuously, maintaining REST endpoints can become increasingly cumbersome, potentially compromising developer experience and speed.

This presentations covers basic concepts of GraphQL and how we integrated it in our Rails application. It includes:

1. Introduction to GraphQL
2. Using GraphQL with Rails
3. Performance optimization of GraphQL endpoints



October 26, 2018


  1. Putting REST to rest with GraphQL RubyConf MY - 26th

    October, 2018
  2. Using and optimising GraphQL

  3. None
  4. None
  5. We started out in early 2015

  6. … and our API journey began with REST

  7. A typical REST endpoint

  8. … and it’s serializer

  9. As we grew, so did our APIs and their clients

  10. API Server The same resource served a different purpose

  11. So we started creating client-specific serializers

  12. ...and allowing clients to specify parameters

  13. /products?params=details /products?params=name&params=price API Server This allowed clients to ask for

    certain attributes /products /products/1/receipt
  14. Clients Server … but neither were happy

  15. Clients often did not have the fields they needed

  16. Server was being overloaded with fields no client was using

  17. So we started exploring GraphQL

  18. Setting up GraphQL was easy honestbee/graphqlapp

  19. We created a Rails-GraphQL app Table Customer Item RESTAURANT APP

  20. … and experimented with our code organization Stores a collection

    of related values e.g. statuses Fields are similar to functions that can accept inputs, resolvers, and return values Reusable container for field logic that can be re-used across many fields Key-value inputs for fields Collection of types which implement the some of the same fields GraphQL::Batch classes to batch database operations in a single query GraphQL middleware Commands that change the state of a system Definitions to expose fields on an object
  21. GraphQL did make the clients happy by... having a strongly

    typed schema (living documentation) clients could specify exactly what they needed clients could fetch all the data with a single request
  22. Clients Server … but the server was still unhappy

  23. GraphQL is not a silver bullet

  24. GraphQL’s additional flexibility comes at a cost - Queries can

    be large, especially when compared to REST - Queries can be constructed maliciously - Queries can be resource intensive
  25. These issues can be improved by practicing caution

  26. Queries can be limited Limit complexity Limit nesting Limit amount

  27. … and whitelisted - APIs cannot be opened to public

    - Queries for outdated clients need to be kept
  28. Sometimes in life, you need to pick your battles

  29. … and we chose to optimize for the health of

    our database
  30. Problem

  31. Problem

  32. The classic N+1 problem

  33. N+1 Problem It's the problem caused when you need to

    make N + 1 SQL Queries, where N is the number of items.
  34. N+1 in graphQL

  35. By default, each association is lazily loaded table get all

    customers customer customer customer get 1 item item get 1 item item get 1 item item
  36. N+1 in REST In REST, it is simpler because for

    each endpoint, you know exactly what the application needs and load only the resources that are needed Customer.includes(:items).where(ids: [...])
  37. N+1 in graphQL - We don't know what is needed

    at the time of writing code as the client can ask for anything they want. - We can't load everything all the time, as it puts unnecessary load on the server and database Customer.includes(????????) Customer.includes(:items) # for every request
  38. Solution?

  39. graphql-batch (A query batching executor for the graphql gem)

  40. table get customers customer customer customer item item item graphQL-batch

    key1 key2 key3 get items using key1,2,3 How graphql-batch helps?
  41. Customer Item 1 * Load items for a list of

  42. Loader

  43. Resolver

  44. Result

  45. Simple filters

  46. Current loader

  47. Add “where” param to loader

  48. Use “where” param

  49. Use “where” param

  50. Use “where” param

  51. None

    / LESSER comparisons
  53. The simple “where” param is not enough

  54. Pass in ActiveRecord::Relation as param

  55. Pass in ActiveRecord::Relation as param

  56. Result

  57. Result

  58. N+1 issue starts again!!!

  59. How does graphql-batch do grouping? customer customer customer item item

    item graphQL-batch key1 key2 key3 get items using key1,2,3
  60. What did we change? # Old # New

  61. We read all the source code!!!

  62. How does graphql-batch do grouping? customer customer customer item item

    item graphQL-batch key1 key2 key3 get items using key1,2,3
  63. Pass in ActiveRecord::Relation as param

  64. Solution 1: make the query object the true same object

    QueryFactory Resolver graphql-batch query args same query object same query object
  65. Query Factory

  66. Query Factory

  67. Query Factory

  68. Query Factory - Implicit behaviour - Hard to enforce developers

    to use the QueryFactory 100% of the times
  69. Solution 2: Overwrite the grouping function GraphQL::Batch::Loader (Parent method) Loaders::CustomValueLoader

    < GraphQL::Batch::Loader (New method)
  70. Overwrite the grouping function

  71. Overwrite the grouping function

  72. Overwrite the grouping function

  73. Overwrite the grouping function - Hacky (change default behaviour of

    the gem) - Hard for new members to understand
  74. Solution 3: Let’s graphql-batch call QueryFactory QueryFactory Resolver graphql-batch query

    args - Require all query services to have standardized contracts - Suitable for newly created projects model model & query args Query service
  75. Takeaways - graphQL is a good alternative of REST -

    Always think about performance when writing resolvers
  76. Thank you! ankitagupta12 SeanNguyen