Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
GraphQL on Rails
Search
Marc-Andre Giroux
September 24, 2016
Programming
2
330
GraphQL on Rails
Marc-Andre Giroux
September 24, 2016
Tweet
Share
More Decks by Marc-Andre Giroux
See All by Marc-Andre Giroux
It Depends - Examining GraphQL Myths & Assumptions
xuorig
0
84
So you Want to Distribute your GraphQL Schema?
xuorig
4
800
So you Want to Distribute your GraphQL Schema?
xuorig
0
490
GraphQL Schema Design @ Scale
xuorig
5
2.1k
Continuous Evolution of GraphQL Schemas @ GitHub
xuorig
3
2k
GraphQL à Shopify
xuorig
0
210
Exploring GraphQL
xuorig
0
250
GraphQL @ Shopify
xuorig
6
1.5k
From REST to GraphQL
xuorig
9
1.2k
Other Decks in Programming
See All in Programming
どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか
okashoi
3
860
GitHub CopilotでTypeScriptの コード生成するワザップ
starfish719
26
5.9k
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
420
アクターシステムに頼らずEvent Sourcingする方法について
j5ik2o
6
700
functionalなアプローチで動的要素を排除する
ryopeko
1
190
Rubyでつくるパケットキャプチャツール
ydah
0
160
shadcn/uiを使ってReactでの開発を加速させよう!
lef237
0
290
KMP와 kotlinx.rpc로 서버와 클라이언트 동기화
kwakeuijin
0
290
BEエンジニアがFEの業務をできるようになるまでにやったこと
yoshida_ryushin
0
180
ISUCON14感想戦で85万点まで頑張ってみた
ponyo877
1
570
良いユニットテストを書こう
mototakatsu
11
3.5k
サーバーゆる勉強会 DBMS の仕組み編
kj455
1
300
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
89
5.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
327
24k
Into the Great Unknown - MozCon
thekraken
34
1.6k
Optimising Largest Contentful Paint
csswizardry
33
3k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.4k
Thoughts on Productivity
jonyablonski
68
4.4k
Statistics for Hackers
jakevdp
797
220k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Making Projects Easy
brettharned
116
6k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
Transcript
GraphQL on Rails Marc-Andre Giroux @__xuorig__
@__xuorig__
@__xuorig__ therakiasite.blogspot.com
@__xuorig__ Montreal, Canada
@__xuorig__
@__xuorig__
@__xuorig__ A simple UI component
@__xuorig__
@__xuorig__ What kind of data is needed?
@__xuorig__ Cart Product ProductImage
@__xuorig__ Reusable endpoints (REST)
@__xuorig__ /carts/1 /products/1 /products/2 /products/3 /product_images/1 /product_images/2 /product_images/3
@__xuorig__ Too many round trips!
@__xuorig__ /carts/1?expand=products
@__xuorig__ /carts/1?fields=products(name, description, price)
@__xuorig__ /carts/1?fields=products/name,description,price
@__xuorig__ Custom Endpoints
@__xuorig__ /cart_with_all_the_stuff_i_need
@__xuorig__
@__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
@__xuorig__
@__xuorig__ Server Client Updates a view Creates a new view
New view version Model changes Update endpoints Create new endpoint
@__xuorig__ yo, give me the resource with id = abc
OK, here’s the resource with id = abc Client Server
@__xuorig__ yo, give me the resource v2 with id =
abc OK, here’s the resource v2 with id = abc Client Server
@__xuorig__ yo, give me the resource v3 with id =
abc OK, here’s the resource v3 with id = abc Client Server
@__xuorig__ GraphQL
@__xuorig__ What GraphQL is NOT
@__xuorig__ What GraphQL IS
@__xuorig__ { myShop { name } } Field Selection Set
@__xuorig__ { myShop { name } } Lexed Parsed Validated
Executed { “myShop” { “name”: “GitHub” } }
@__xuorig__ { myShop { name } }
@__xuorig__ { myShop { name } }
@__xuorig__
@__xuorig__ { shop(id: 1) { name } }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ { “myShop”: { “name”: “Euruko 2016” “location” { “city”:
“Sofia” “address”: “Av. Diagonal 547” } “products”: [{ “name”: “Conference Ticket” “price”: 500000 }, { “name”: “Cool T-Shirt” “price”: 20000 }] } }
@__xuorig__ Type System
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ type QueryRoot { myShop: Shop shop(id: Int): Shop }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ type Shop { name: String location: Address products(orderby: OrderEnum):
[Product] } enum ProductOrderEnum { PRICE, POPULARITY, ALPHABETICAL }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ type Address { city: String address: String }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { name price } } }
@__xuorig__ type Product { name: String price: Int }
@__xuorig__ { myShop { name yolo { swag } }
}
@__xuorig__ { myShop { name yolo { swag } }
} type Shop { name: String location: Address }
@__xuorig__ Introspection
@__xuorig__ query { __schema { … } }
@__xuorig__ Static Validation Code Generation IDE Integration Auto Documentation
@__xuorig__
@__xuorig__
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { id name price } } }
@__xuorig__
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { id name price } } }
@__xuorig__ Fragments
@__xuorig__ fragment productFields on Product { id name price }
@__xuorig__ { myShop { name location { city address }
products(orderby: POPULARITY) { ...productFields } } }
@__xuorig__ query Fragment Fragment Fragment Fragment
@__xuorig__ Mutations
@__xuorig__ mutation { createProduct(name: “Nice Mug”, price: 10000) { id
name } }
@__xuorig__ Resolving fields
@__xuorig__ github.com/rmosolgo/graphql-ruby
@__xuorig__ ProductType = GraphQL::ObjectType.define do name "Product" description “A product
sold at a shop” # … end
@__xuorig__ field :name do type types.String resolve -> (obj, args,
ctx) { obj.name } end
@__xuorig__ field :price do type types.Int resolve -> (obj, args,
ctx) do obj.subtotal + obj.taxes + obj.shipping_price end end
@__xuorig__ ShopType = ObjectType.define do name “Shop" description "A Shop"
field :products, [ProductType] end
@__xuorig__ QueryType = ObjectType.define do name “Query" description “The Root
of my Schema" field :myShop, ShopType end
@__xuorig__ MySchema = GraphQL::Schema.new( query: QueryRoot, mutation: MutationRoot )
@__xuorig__ POST /graphql
@__xuorig__ module Api class GraphqlController < ApiController def query query_string
= params[:query] variables = params[:variables] context = { current_user: current_user } # ... end end end
@__xuorig__ module Api class GraphqlController < ApiController def query #
... result = MySchema.query( query_string, variables: variables, context: context ) render json: result end end end
@__xuorig__ Drawbacks and solutions
@__xuorig__ N+1 Queries
@__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
@__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" = …
@__xuorig__ Solution: Batching + Caching
@__xuorig__ field :image do type ImageType resolve -> (product, args,
ctx) do RecordLoader.for(Image).load(product.image_id) end end
@__xuorig__ HTTP Caching
@__xuorig__ Solution: Client Side Cache
@__xuorig__ Normalized Cache
@__xuorig__ query { shop { products { price } }
} query { shop { product(id: 1) { price } } }
@__xuorig__ { root: { shop: { products: [ Link.new(1) ]
} }, 1: { price: 1000 } }
@__xuorig__ https://github.com/facebook/relay http://www.apollostack.com/
@__xuorig__ Security
@__xuorig__ Query Depth Timeouts Query Complexity
@__xuorig__ Future
@__xuorig__ Subscriptions
@__xuorig__ subscription { productInventorySubscribe { products { inventory } }
}
@__xuorig__ Deferred Queries
@__xuorig__ query { shop { name description products { name
price } } }
@__xuorig__ query { shop { name description products { name
price } } }
@__xuorig__ query { shop { name description products @defer {
name price } } }
@__xuorig__
@__xuorig__ yo, give me the resource with id = abc
OK, here’s the resource with id = abc Client Server
@__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
@__xuorig__ oh boy :( Client Server
@__xuorig__ I need that exact data shape (requirements) Here is
everything you can do! (capabilities) Client Server
@__xuorig__ Efficiency
@__xuorig__ Predictability
@__xuorig__ Thank you! Marc-Andre Giroux @__xuorig__