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

Optimizing APIs for Consumers with GraphQL

Optimizing APIs for Consumers with GraphQL

Presented at Goruco

359024e7132672aaeef4a3e792be4ae5?s=128

Brooks Swinnerton

June 24, 2017
Tweet

Transcript

  1. None
  2. Hi, I’m Brooks

  3. I work at !

  4. optimizing APIs for consumers…

  5. with GraphQL

  6. let’s start with APIs

  7. REST APIs

  8. http://nyc-restaurant-grades.com

  9. http://nyc-restaurant-grades.com/api/v1/restaurants GET

  10. !"""# verb http://nyc-restaurant-grades.com/api/v1/restaurants GET

  11. !"""""""""""""""""""""""""""""""""""""""""""""""""""# endpoint !"""# verb http://nyc-restaurant-grades.com/api/v1/restaurants GET

  12. http://nyc-restaurant-grades.com/api/v1/restaurants GET

  13. [ { "id": 1, "name": "Garden Of Eat In", "grade":

    "A", "camis": "40394054", "address": "1416 Avenue J, Brooklyn, New York 11230", "cuisine": "Jewish/Kosher", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1", "inspections_url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections" }, { "id": 2, "name": "Hunan Delight", "grade": "A", "camis": "41188631", "address": "752 Union Street, Brooklyn, New York 11215", "cuisine": "Chinese", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/2", "inspections_url": "http://nyc-restaurant-grades.com/api/v1/restaurants/2/inspections" }, ... ] http://nyc-restaurant-grades.com/api/v1/restaurants GET
  14. REST APIs return resources

  15. enhance… !

  16. [ { "id": 1, "name": "Garden Of Eat In", "grade":

    "A", "camis": "40394054", "address": "1416 Avenue J, Brooklyn, New York 11230", "cuisine": "Jewish/Kosher", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1", "inspections_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections" }, ... ] http://nyc-restaurant-grades.com/api/v1/restaurants GET
  17. [ { "id": 1, "name": "Garden Of Eat In", "grade":

    "A", "camis": "40394054", "address": "1416 Avenue J, Brooklyn, New York 11230", "cuisine": "Jewish/Kosher", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1", "inspections_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections" }, ... ] http://nyc-restaurant-grades.com/api/v1/restaurants GET
  18. [ { "id": 1, "name": "Garden Of Eat In", "grade":

    "A", "camis": "40394054", "address": "1416 Avenue J, Brooklyn, New York 11230", "cuisine": "Jewish/Kosher", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1", "inspections_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections" }, ... ] http://nyc-restaurant-grades.com/api/v1/restaurants GET
  19. http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections GET

  20. http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections GET

  21. [ { "id": 1, "type": "Trans Fat / Initial Inspection",

    "score": 9, "grade": "A", "inspected_at": "2016-01-27T00:00:00.000Z", "graded_at": "2016-01-27T00:00:00.000Z", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1/ inspections/1", "restaurant_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1", "violations_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections/1/violations" } ] http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections GET
  22. [ { "id": 1, "type": "Trans Fat / Initial Inspection",

    "score": 9, "grade": "A", "inspected_at": "2016-01-27T00:00:00.000Z", "graded_at": "2016-01-27T00:00:00.000Z", "url": "http://nyc-restaurant-grades.com/api/v1/restaurants/1/ inspections/1", "restaurant_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1", "violations_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections/1/violations" } ] http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections GET
  23. http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections/1/violations GET

  24. http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections/1/violations GET

  25. http://nyc-restaurant-grades.com/api/v1/restaurants/1/inspections/1/violations GET [ { "id": 62855, "description": "Food not protected

    from potential source of contamination during storage, preparation, transportation, display or service.", "code": "06C", "url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections/29572/violations/62855", "inspection_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1/inspections/29572", "restaurant_url": "http://nyc-restaurant-grades.com/api/v1/ restaurants/1" } ]
  26. API Server /restaurants /inspections /violations

  27. API Server /restaurants /inspections /violations

  28. API Server /restaurants /inspections /violations

  29. API Server /restaurants /inspections /violations

  30. API Server /restaurants /inspections /violations

  31. "RESTful APIs are optimized for servers, not clients." - Mark

    Twain (I think)
  32. what if we wanted to put clients first?

  33. backends for frontends

  34. API Server /desktop /mobile /fridge

  35. API Server /desktop /mobile /fridge

  36. but this only works if you’re in control of the

    client
  37. what about a public API?

  38. enter GraphQL

  39. a data query language

  40. think SQL

  41. not Neo4j

  42. GraphQL is a specification

  43. { me { firstName lastName email } }

  44. { me { firstName lastName email } }

  45. { me { firstName lastName email } }

  46. { me { firstName lastName email } } { "data":{

    "me":{ "firstName":"Brooks", "lastName":"Swinnerton", "email":"brooks@github.com" } } }
  47. https://nyc-restaurant-grades.com/graphql POST

  48. !"""# verb https://nyc-restaurant-grades.com/graphql POST

  49. !"""# verb !""""""""""""""""""""""""""""""""""""""""# endpoint https://nyc-restaurant-grades.com/graphql POST

  50. https://nyc-restaurant-grades.com/graphql POST

  51. { restaurant(name: "Cafe Ghia") { name cuisine } } https://nyc-restaurant-grades.com/graphql

    POST
  52. { restaurant(name: "Cafe Ghia") { name cuisine } } https://nyc-restaurant-grades.com/graphql

    POST
  53. { restaurant(name: "Cafe Ghia") { name cuisine } } {

    "data": { "restaurant": { "name": "Cafe Ghia", "cuisine": "American" } } } https://nyc-restaurant-grades.com/graphql POST
  54. features of the query language

  55. GraphQL is typed

  56. { me { firstName lastName email } } type RootQuery

    { me: User } type User { firstName: String lastName: String email: String }
  57. { me { firstName lastName email } } type RootQuery

    { me: User } type User { firstName: String lastName: String email: String }
  58. { me { firstName lastName email } } type RootQuery

    { me: User } type User { firstName: String lastName: String email: String }
  59. type RootQuery { restaurant(name: String): Restaurant } type Restaurant {

    name: String! cuisine: String } { restaurant(name: "Cafe Ghia") { name cuisine } }
  60. type RootQuery { restaurant(name: String): Restaurant } type Restaurant {

    name: String! cuisine: String } { restaurant(name: "Cafe Ghia") { name cuisine } }
  61. type RootQuery { restaurant(name: String): Restaurant } type Restaurant {

    name: String! cuisine: String } { restaurant(name: "Cafe Ghia") { name cuisine } }
  62. type RootQuery { restaurant(name: String): Restaurant } type Restaurant {

    name: String! cuisine: String } { restaurant(name: "Cafe Ghia") { name cuisine } }
  63. { restaurants(borough: BROOKLYN) { name cuisine } }

  64. { restaurants(borough: BROOKLYN) { name cuisine } }

  65. { restaurants(borough: BROOKLYN) { name cuisine } }

  66. type RootQuery { restaurants(borough: RestaurantBorough): [Restaurant]! } enum RestaurantBorough {

    BRONX BROOKLYN MANHATTAN STATEN_ISLAND QUEENS } type Restaurant { name: String! cuisine: String }
  67. type RootQuery { restaurants(borough: RestaurantBorough): [Restaurant]! } enum RestaurantBorough {

    BRONX BROOKLYN MANHATTAN STATEN_ISLAND QUEENS } type Restaurant { name: String! cuisine: String }
  68. type RootQuery { restaurants(borough: RestaurantBorough): [Restaurant]! } enum RestaurantBorough {

    BRONX BROOKLYN MANHATTAN STATEN_ISLAND QUEENS } type Restaurant { name: String! cuisine: String }
  69. { restaurant(name: "Mcdonalds") { name cuisine } restaurant(name: "Wendy's") {

    name cuisine } }
  70. { restaurant(name: "Mcdonalds") { name cuisine } restaurant(name: "Wendy's") {

    name cuisine } } { "data": { "restaurant": { "name": "Mcdonalds", "cuisine": "American" }, "restaurant": { "name": "Wendy's", "cuisine": "American" } } }
  71. { restaurant(name: "Mcdonalds") { name cuisine } restaurant(name: "Wendy's") {

    name cuisine } } { "data": { "restaurant": { "name": "Mcdonalds", "cuisine": "American" }, "restaurant": { "name": "Wendy's", "cuisine": "American" } } }
  72. { mcdonalds: restaurant(name: "McDonalds") { name cuisine } wendys: restaurant(name:

    "Wendy's") { name cuisine } }
  73. { "data": { "mcdonalds": { "name": "Mcdonalds", "cuisine": "American" },

    "wendys": { "name": "Wendy's", "cuisine": "American" } } }
  74. { mcdonalds: restaurant(name: "McDonalds") { name cuisine } wendys: restaurant(name:

    "Wendy's") { name cuisine } }
  75. { mcdonalds: restaurant(name: "McDonalds") { ...RestaurantInfo } wendys: restaurant(name: "Wendy's")

    { ...RestaurantInfo } } fragment RestaurantInfo on Restaurant { name cuisine }
  76. GraphQL is introspectable

  77. documentation and client generation, are free

  78. None
  79. multiple resources in one round trip

  80. API Server /restaurants /inspections /violations

  81. API Server /restaurants /inspections /violations

  82. API Server /restaurants /inspections /violations

  83. API Server /restaurants /inspections /violations

  84. API Server /desktop /mobile /fridge

  85. API Server /desktop /mobile /fridge

  86. API Server /graphql

  87. API Server /graphql

  88. Implementing a GraphQL server

  89. https://nyc-restaurant-grades.com/graphql POST

  90. class GraphqlController < ApplicationController def create result = Graph::Schema.execute(params[:query]) render

    json: result end end app/controllers/graphql_controller.rb
  91. class GraphqlController < ApplicationController def create result = Graph::Schema.execute(params[:query]) render

    json: result end end app/controllers/graphql_controller.rb
  92. class GraphqlController < ApplicationController def create result = Graph::Schema.execute(params[:query]) render

    json: result end end app/controllers/graphql_controller.rb
  93. like SQL, there are many implementations

  94. let’s talk about the Ruby implementation

  95. rmosolgo/graphql-ruby

  96. { restaurant(name: "Cafe Ghia") { name cuisine } }

  97. { restaurant(name: "Cafe Ghia") { name cuisine } } type

    RootQuery { restaurant(name: String): Restaurant } type Restaurant { name: String! cuisine: String }
  98. type RootQuery { restaurant(name: String): Restaurant } ?

  99. ? type RootQuery { restaurant(name: String): Restaurant }

  100. type RootQuery { restaurant(name: String): Restaurant } module Graph::Types RootQuery

    = GraphQL::ObjectType.define do name 'RootQuery' end end
  101. # The query root. type RootQuery { restaurant(name: String): Restaurant

    } module Graph::Types RootQuery = GraphQL::ObjectType.define do name 'RootQuery' description 'The query root.' end end
  102. type RootQuery { restaurant(name: String): Restaurant } ?

  103. module Graph::Types RootQuery = GraphQL::ObjectType.define do name 'RootQuery' field :restaurant

    do argument :name, types.String type Graph::Types::Restaurant resolve -> (object, arguments, context) do ::Restaurant.find_by(name: arguments['name']) end end end end type RootQuery { restaurant(name: String): Restaurant }
  104. module Graph::Types RootQuery = GraphQL::ObjectType.define do name 'RootQuery' field :restaurant

    do argument :name, types.String type Graph::Types::Restaurant resolve -> (object, arguments, context) do ::Restaurant.find_by(name: arguments['name']) end end end end type RootQuery { restaurant(name: String): Restaurant }
  105. module Graph::Types RootQuery = GraphQL::ObjectType.define do name 'RootQuery' field :restaurant

    do argument :name, types.String type Graph::Types::Restaurant resolve -> (object, arguments, context) do ::Restaurant.find_by(name: arguments['name']) end end end end type RootQuery { restaurant(name: String): Restaurant }
  106. module Graph::Types RootQuery = GraphQL::ObjectType.define do name 'RootQuery' field :restaurant

    do argument :name, types.String type Graph::Types::Restaurant resolve -> (object, arguments, context) do ::Restaurant.find_by(name: arguments['name']) end end end end type RootQuery { restaurant(name: String): Restaurant }
  107. type Restaurant { name: String! cuisine: String } ?

  108. type Restaurant { name: String! cuisine: String } module Graph::Types

    Restaurant = GraphQL::ObjectType.define do name 'Restaurant' end end
  109. # A place of business serving food. type Restaurant {

    name: String cuisine: String } module Graph::Types Restaurant = GraphQL::ObjectType.define do name 'Restaurant' description 'A place of business serving food.' end end
  110. type Restaurant { name: String cuisine: String } ?

  111. type Restaurant { name: String cuisine: String } module Graph::Types

    Restaurant = GraphQL::ObjectType.define do name 'Restaurant' field :cuisine do type types.String resolve -> (restaurant, arguments, context) do restaurant.cuisine end end end end
  112. type Restaurant { name: String cuisine: String } module Graph::Types

    Restaurant = GraphQL::ObjectType.define do name 'Restaurant' field :cuisine do type types.String resolve -> (restaurant, arguments, context) do restaurant.cuisine end end end end
  113. type Restaurant { name: String cuisine: String } module Graph::Types

    Restaurant = GraphQL::ObjectType.define do name 'Restaurant' field :cuisine do type types.String resolve -> (restaurant, arguments, context) do restaurant.cuisine end end end end
  114. GraphQL schemas have resolvers

  115. data can be resolved from ✨anywhere ✨

  116. Which makes GraphQL servers…

  117. a facade.

  118. a facade in front of databases

  119. a facade in front of caches

  120. a facade in front of services

  121. you use your existing application code

  122. GraphQL at GitHub

  123. the history of GitHub’s API

  124. !"""# verb !"""""""""""""""""""""""# endpoint https://api.github.com GET

  125. v4?

  126. March 20th, 2016 proposal submitted

  127. April 6th, 2016 proof of concept done

  128. April 12th, 2016 New team created

  129. September 14th, 2016 early access

  130. Today >100 million queries/day

  131. we use GraphQL both…

  132. externally

  133. !"""# verb !""""""""""""""""""""""""""""""# endpoint https://api.github.com/graphql POST

  134. and internally

  135. github/graphql-client

  136. exchange a query for Ruby objects

  137. collocate our queries in our views

  138. tooling

  139. gjtorikian/graphql-docs

  140. https://github.com/gjtorikian/graphql-docs

  141. schema driven development

  142. with our REST API

  143. new features were developed for the UI

  144. then staff shipped

  145. then released

  146. REST API work started after the release

  147. but today…

  148. all new features are built with GraphQL

  149. from the start

  150. this allows us to build a true public API

  151. this allows us to build a true public API

  152. this allows us to build a true platform

  153. shared between GitHubbers and integrators

  154. what if we wanted to put clients first?

  155. thank you @bswinnerton

  156. More information

  157. http://nyc-restaurant-grades.com @bswinnerton

  158. http://graphql.org @bswinnerton