DAY WUN SLIDES http://slides.com/wbruce/production-ready-graphql-1#/ https://github.com/sabondano/elixirconf-2018
Root types: query, mutation, subscription
- Absinthe will barf out the introspection query. You can run this query directly by pasting it into the playground
- Resolution errors are isolated between siblings. IE if
user {
name # This will come back ok
email # this can be an error and it will be ok
}
- If the query itself is invalid you won't see errors and data
user {
name # This will come back ok
foo # doesn't exist
}
-
HTTP response codes: You will still get a 200 even if there are errors. If the query is wrong then you'll get a 400. This is an example of how REST is tightly coupled to HTTP and graphql is not. We need to get used to not working based on http status codes for errors
-
In a list_of, the error will give the index in the list of the resource with the error
- Resolvers are run once for each parent
- Resolvers are never called if it is not asked for in the query
- They have a lot in common with controllers (controllers do http, validation, and data fetch. Resolvers are just data fetch but still similar)
- Absinthe.Resolution.project(resolution) gives the requested child fields
- The third argument is the resolution
- Lots of options. Can do default values, required, etc
- Why input_object AND objects?
- An input object does not allow cycles
- Maybe a bit more like an echo changeset
- Allows separation of things like flags that aren't actually supposed to be persisted
- Get your head out of weird RESTY names. You're not constrained by that anymore
- Logger won't filter out things that are not in variables
- Subscription storage is not pluggable
- You can manually trigger subscriptions from resolver code. Good for when the data change is not being triggered by a graphql mutation
How do we communicate the token? HTTP headers are a very http concern
- Split out any kind of db access, etc and try to write pure functions to check things
- Every level of the query has a list of middleware. For most it is just the Absinthe.Resolution middleware
- You can conditionally add middleware by pattern matching on the field or the object
Auth on subscriptions
- Using "relay style" you can pack random poop into each edge
- edge is a wrapper for an item and some metadata
- node is the item itself
- Complexity analysis is totally static (not based on data). It's all about potential complexity rather than what data is actually there
- Think of complexity limits as a guide for real, well intentioned users
- To annotate complexity, just use the "complexity" macro in a field do block
- The code for the async middleware is an instructive read
- Use batch to use an arbitrary function to return data to each resolver
- Async will do async but will not batch
- Batching works by key and so we can batch queries for like things across the query. This is the best way to do service aggregation
- Batches are all done concurrently
- Recommend wrapping batching in more meaningful method names
- Dataloader aims to solve the simple case of batches by id
- Dataloader predates absinthe
- We probably almost never want data loader
- The comparison is more applicable to SQL maybe than REST
- Third party integrations are not as hard as you'd think with folks who don't know GraphQL
- Think of the relational db Layer as a "serialization" of your graph
- SQL Schema for ease of your business logic, grapnel for ease of your users
- Graphql itself is transport agnostic
- Resolvers are per-field. Easy to do n+1
- Not easy to do the right thing
- Adds ~100ms latency
- If it always makes sense to be scoping things through permissions. These are things you should pass into the business logic of your data
- Instead of making every node ask if the current user is allowed to do something, you want to block an unauthorized user from walking past a certain point on the graph. Therefore it may be better to drop privdeledge fields further down the graph
- Good practice to list as a dependency any library which is actually called