This guide is going to grow as we develop more tooling around Ecto integrations (it’s a priority on our roadmap, but there are some important things we can point out now that might be helpful.
Avoiding N+1 Queries
In general, you want to make sure that when accessing Ecto associations that you preload the data in the top level resolver functions to avoid N+1 queries.
Imagine this scenario: You have posts and users. A Post has an author field, which returns a user. You want to list all posts, and get the name of their author
If you write your schema like this, you’re going to have a bad time:
What this schema will do when presented with the GraphQL query is
Post |> Repo.all, which will retrieve N posts. Then for each
post it will resolve child fields, which runs our
function, resulting in N+1 calls to the database.
Instead, use batching! At the moment (Oct-31-2016) Batching is pretty new, so we don’t yet have some of the helper functions we want to in order to make this easier.
Fortunately the batching API is pretty simple. The idea with batching is that we’re
gonna aggregate all the
author_ids from each post, and then make one call to the user.
Let’s first make a function to get a model by ids.
Now we can use this function to batch our author lookups:
Now we make just two calls to the database. The first call loads all of the posts. Then as Absinthe walks through each post and tries to get the author, it’s instead told to aggregate its information.
That aggregate information is passed on to our
by_id/2 function from earlier.
It grabs ALL the users in just one database call, and creates a map of user ids
Absinthe then does a second pass and calls the
batch_results function with that
map, letting us retrieve the individual author for each post.
Not only is this a very efficient way to query the data, it’s also 100% dynamic. If a query document asks for authors, they’re loaded efficiently. If it does not, they aren’t loaded at all.
See FIXME link
batch API above is a bit verbose. This verbosity happens because it’s very
generic, so you gotta give it the individual bits and pieces. However for Ecto
associations specifically, you can easily see how the code we have above could be
made more succinct by using information we already have on our Ecto schemas.
Thus what we hope to have soon in Absinthe.Ecto (doesn’t exist yet) are functions that let you do something like:
belongs_to function would derive the right batching approach based on the
Ecto association. These functions are mere conveniences. Everything they would do
functionally is available to you today!