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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Marc-Andre Giroux
September 24, 2016
Programming
400
2
Share
GraphQL on Rails
Marc-Andre Giroux
September 24, 2016
More Decks by Marc-Andre Giroux
See All by Marc-Andre Giroux
It Depends - Examining GraphQL Myths & Assumptions
xuorig
0
130
So you Want to Distribute your GraphQL Schema?
xuorig
4
880
So you Want to Distribute your GraphQL Schema?
xuorig
0
690
GraphQL Schema Design @ Scale
xuorig
5
2.2k
Continuous Evolution of GraphQL Schemas @ GitHub
xuorig
3
2.2k
GraphQL à Shopify
xuorig
0
280
Exploring GraphQL
xuorig
0
310
GraphQL @ Shopify
xuorig
6
1.8k
From REST to GraphQL
xuorig
9
1.3k
Other Decks in Programming
See All in Programming
dRuby over BLE
makicamel
2
300
oxlintはeslint/typescript-eslintを置き換えられるのか
shomafujita
2
310
Lessons from Spec-Driven Development
simas
PRO
0
130
These Five Tricks Can Make Your Apps Greener, Cheaper, & Nicer
hollycummins
0
270
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
130
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
160
Stage 3 Decorators でできること / できないこと / TSKaigi 2026
susisu
1
1.5k
Technical Debt: Understanding it Rightly, Engaging it Rightly #LaravelLiveJP
shogogg
0
190
ビジネスモデルから紐解く、AI+型駆動開発
hirokiomote
2
5.2k
Migrations : C'est une question d'hygiène !
vinceamstoutz
0
3.2k
LLM Plugin for Node-REDの利用方法と開発について
404background
0
160
タクシーアプリ『GO』の バックエンド開発のおける AI利活用と若者のすべて
pyama86
3
1.8k
Featured
See All Featured
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
220
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
320
The SEO identity crisis: Don't let AI make you average
varn
0
480
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
280
We Have a Design System, Now What?
morganepeng
55
8.2k
BBQ
matthewcrist
89
10k
Leo the Paperboy
mayatellez
7
1.8k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
How to train your dragon (web standard)
notwaldorf
97
6.7k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
260
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__