GraphQL

Write an awesome doc for GraphQL. A very nice an practical one extracted from GraphQL official documentation.

View on GitHub

Batching technique:

Dataloader

NestJS + Dataloader

  1. pnpm add dataloader
    
  2. nx g @nx/node:app --framework nest --directory apps/dataloader-example
    nest g module dataloader
    nest g interface dataloader
    
  3. Create a new function that load the data in batch in your repository level like what we did here and here.
  4. You also need to add a new method to your service layer as we did here for example.
  5. nest g service dataloader --no-spec
    

    Lastly you need to create a data loader for the things that needs to be batched together.

    [!CAUTION]

    Note that we do not need to make this service request scoped, i.e. we do not need to annotate the newly created service with @Injectable({ scope: Scope.REQUEST }). As you can see it clearly in your terminal that DataloaderService’s constructor is being called again and again for each new request :slightly_smiling_face:.

    You can see it by running apps/dataloader-example app and checking the logs (code).

Provide a function that is your batch function:

[!NOTE]

When calling the Dataloader to load data (e.g. user.loader(1)), Dataloader will batch this query with other calls that happened in the same tick of event loop.

You can also pass an options to it:

[!TIP]

My implementation in apps/dataloader-example was purely experimental and in a real world app I believe using a lib would be much better. Something like: @strv/nestjs-dataloader (although they’ve changed the license from MIT to BSD-3!) or nestjs-dataloader.

Caching in Dataloader

Event Loop + Dataloader + Promise

[!CAUTION]

NodeJS has two Microtasks queues. So in order to run something after promises you need to make sure that you’re calling process.nextTick after all promises are resolved.


console.log('Start...');
setTimeout(() => console.log('Timeout'), 0);
function a() {
  return new Promise((res) => res());
}
function b() {
  return new Promise((res) => {
    console.log('Next ticking!');
    process.nextTick(res);
  });
}
b().then(
  console.log.bind(this, 'First promise call is next ticked!'),
);
a().then(console.log.bind(this, 'Second promise call'));
console.log('...Finished');


console.log("Start...");
setTimeout(() => console.log("Timeout"), 0);
function a() {
  return new Promise((res) => res());
}
function b() {
  return new Promise((res) => {
    console.log("Next ticking!");
    process.nextTick(res);
  });
}
// Assume this is placed somewhere else in your codebase
(async () => {
  await b();
  console.log("First promise call is next ticked!");
})();
// Assume this is placed somewhere else in your codebase
(async () => {
  await a();
  console.log("Second promise call");
})();
console.log("...Finished");


Start...
Next ticking!
...Finished
Second promise call
First promise call is next ticked!
Timeout


Start...
Next ticking!
...Finished
Second promise call
First promise call is next ticked!
Timeout

Ref