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
380
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
110
So you Want to Distribute your GraphQL Schema?
xuorig
4
860
So you Want to Distribute your GraphQL Schema?
xuorig
0
660
GraphQL Schema Design @ Scale
xuorig
5
2.2k
Continuous Evolution of GraphQL Schemas @ GitHub
xuorig
3
2.1k
GraphQL à Shopify
xuorig
0
260
Exploring GraphQL
xuorig
0
290
GraphQL @ Shopify
xuorig
6
1.8k
From REST to GraphQL
xuorig
9
1.3k
Other Decks in Programming
See All in Programming
それ、本当に安全? ファイルアップロードで見落としがちなセキュリティリスクと対策
penpeen
7
2.4k
AI によるインシデント初動調査の自動化を行う AI インシデントコマンダーを作った話
azukiazusa1
1
580
高速開発のためのコード整理術
sutetotanuki
1
330
AgentCoreとHuman in the Loop
har1101
5
200
gunshi
kazupon
1
140
実は歴史的なアップデートだと思う AWS Interconnect - multicloud
maroon1st
0
360
Grafana:建立系統全知視角的捷徑
blueswen
0
310
Unicodeどうしてる? PHPから見たUnicode対応と他言語での対応についてのお伺い
youkidearitai
PRO
0
980
2年のAppleウォレットパス開発の振り返り
muno92
PRO
0
190
TerraformとStrands AgentsでAmazon Bedrock AgentCoreのSSO認証付きエージェントを量産しよう!
neruneruo
4
2.6k
CSC307 Lecture 04
javiergs
PRO
0
650
Honoを使ったリモートMCPサーバでAIツールとの連携を加速させる!
tosuri13
1
170
Featured
See All Featured
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
190
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.6k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
3.6k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.3k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.1k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
0
2.3k
RailsConf 2023
tenderlove
30
1.3k
Code Review Best Practice
trishagee
74
19k
AI: The stuff that nobody shows you
jnunemaker
PRO
2
200
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
72
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
82
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__