web APIs It began as an internal Facebook project in 2012 Bad Internet connectivity Many di erent mobile apps No more overfetching and underfetching Less round-trips and smaller payloads 9 / 45
teams working in parallel to a contract (schema) We bene ted from this The frontend team implemented a mock API until backend was done Actual integration after 1 month was okay- ish 16 / 45
requirements. 1. Query returns data to the caller should be cacheable no validation errors needed GraphQL initially only had this 2. Command performs a mutable action needs validation errors GraphQL added these afterwards in the form of Mutations 22 / 45
operation that can be authorized class PostsController < ApiController def create # First, check the client's permission level: if current_user.can?(:create_posts) # If the user is permitted, then perform the action: post = Post.create(params) render json: post else # Otherwise, return an error: render nothing: true, status: 403 end end end 27 / 45
to GraphQL Only one controller and one action class GraphqlController < ApplicationController def execute # What permission is required for `query_str`? # It depends on the string! So, you can't generalize at this level. if current_user.can?(:"???") MySchema.execute(query_str, context: ctx, variables: variables) end end end 28 / 45
for authorization. Mutations each mutation is like an API request in itself previous example of Posts#create will map to the createPost(...) mutation so each mutation should be authorized in its own right should return errors Queries each individual object can be like a GET request to a REST API so each object should be authorized for reading in its own right instead of errors, parts of the response will be null 29 / 45
testing See https://graphql.org/learn/authorization/ class Post < ActiveRecord::Base # Return the list of posts which `user` may see def self.posts_for(user) if user.admin? self.all else self.published end end end field :posts, [Types::Post], null: false def posts # Fetch the posts this user can see: Post.posts_for(context[:current_user]) end 30 / 45
query nested objects it's not necessarily a N+1 SQL query problem, you could have di erent backend services for fetching each of the depdendent objects some sort of batched data loading is needed Facebook's DataLoader JS reference implementation: https://github.com/graphql/dataloader 32 / 45
which changes based on the viewer (current user) class Types::ArticleType < GraphQL::Schema::Object graphql_name 'Article' # ... field :viewer_has_favorited, Boolean, null: false def viewer_has_favorited current_user = context[:current_user] return false unless current_user # When multiple articles are queried, this will cause N+1 queries current_user.favorites.find_by(article_id: object.id).present? end end Note to self: show the Rails logs 33 / 45
... def viewer_has_favorited # ... Loaders::FavoritesLoader.for(current_user).load(object.id) end end Note to self: show the Rails logs class Loaders::FavoritesLoader < GraphQL::Batch::Loader def initialize(current_user) @current_user = current_user end def perform(ids) favorite_article_ids = @current_user.favorites.where(article_id: ids).pluck(:a ids.each { |id| fulfill(id, favorite_article_ids.include?(id)) } end end 34 / 45
doesn't have this yet :'( The idea is to use tracing to track which deprecated elds have stopped being used by clients so that they can be removed permanently Please, please avoid breaking changes in your APIs like the plague! 37 / 45
your GraphQL API Easily spot performance bottlenecks graphql-ruby has several integrations out-of-the-box Appsignal New Relic Scout Skylight Datadog Prometheus 38 / 45
fetchPolicy Globally unique cache key for each Object, can be a combination of typename and ID I like to think of my data graph like an RTS game map with fog of war :) Server-side caching More tricky, but could be done with a similar per-Object strategy Cache invalidation is even trickier, a simple solution would be to use short cache expiration 41 / 45
and strongly-typed- ness, GraphQL has a ton of tools built around it. GraphiQL https://www.apollographql.com/ https://github.com/apollographql/graphql-tools https://hasura.io/all-features 42 / 45
what it isn't common use cases: many clients contract-based development dynamic UI Command vs. Query Authorization, N+1 Versioning, Tracing, Caching We will soon be deprecated as programmers thanks to tools like Hasura 43 / 45