Solving N + 1 problem with GraphQL, TypeScript, and PostgresQL
Wonder2210
Posted on June 23, 2020
One of the common problems that most of the developers face with graphql, is the N + 1 problem, it means that the server executes multiple unnecessary round trips to datastores for nested data, resulting in slow response times and expensive requests
For Example
graphql
query{
users{
id,
full_name,
pets{
name
}
}
}
in the query above, will be executed 1 query to the database to the datastore get the users and for each user another extra query for each user to fetch its pets (N queries for N users) resulting in N + 1.
Now What to do?
to solve this problem the Graphql team in Facebook created Dataloader
wich batch our data, AKA: request all the data from the database, at the start of the server, and when we are going to request one item, all that we do is pass down the identifier of the item and execute a search inside the array of data, which is a lot faster than execute a query to the database every time is made a request because is retrieving data from memory not datastore
Once this is clarified let's puts our hands on
for this post I'll be using the API example I use for my last post after clone into your PC the repo and install everything
run:
bash
yarn add dataloader
</code>
create the following folder and file
...
src/
utils/
loaders.ts
...
and in uutils/loaders.ts
import {BatchLoadFn} from 'dataloader';
import {Pet,User} from '../database/models';
export const Pets : BatchLoadFn<number,Array<Pet>>= async (ids)=>{
const pets= await Pet.query();
return ids.map(i=>pets.filter(item=> item.id===i))
};
export const Users : BatchLoadFn<number,Array<User>> = async (ids)=>{
const users = await User.query();
return ids.map(id=> users.filter(i=> i.id===id));
}
the way it will be used in our resolvers is through the context :
src/index.ts
typescript
...
import DataLoader from 'dataloader';
import {Pets,Users} from './utils/loaders';
...
...
const config : Config = {
schema:schema,
introspection: true,//these lines are required to use the gui
playground: true,// of playground
context:{
loaders:{
users: new DataLoader(Users),
pets: new DataLoader(Pets),
}
}
}
...
now in your resolvers instead of using a query to get the data nested
you use the loaders passed down through the context and using the id to find the match in the loaders
And we can appreciate an increase of performance in the response
Thank's for read it , if you have any suggestion for me I would love to know it, leave it below in the comments box, if you liked it , please follow me here and also on twitter, Thanks! take care!
Posted on June 23, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
July 24, 2021