v5 (latest)FeaturesExplicit Resource Management

Explicit Resource Management

While implementing your GraphQL server with GraphQL Yoga, you need to control over the lifecycle of your resources. This is especially important when you are dealing with resources that need to be cleaned up when they are no longer needed, or clean up the operations in a queue when the server is shutting down.

Dispose GraphQL Yoga

GraphQL Yoga supports Explicit Resource Management approach that allows you to dispose of resources when they are no longer needed. This can be done in two ways shown below;

We use the await using syntax to create a new instance of yoga and dispose of it when the block is exited. Notice that we are using a block to limit the scope of yoga within { }. So resources will be disposed of when the block is exited.

console.log('Yoga is starting')
{
    await using yoga = createYoga({
        schema: createSchema({
            typeDefs: /* GraphQL */ `
                type Query {
                    hello: String
                }
            `,
            resolvers: {
                Query: {
                    hello: () => 'world'
                }
            }
        })
    });
}
console.log('Yoga is disposed')

In the first example, we use the await using syntax to create a new instance of yoga and dispose of it when the block is exited. In the second example,

Dispose on Node.js

When running GraphQL Yoga on Node.js, you can use process event listeners or server’s close event to trigger GraphQL Yoga’s disposal. Or you can configure GraphQL Yoga to handle this automatically by listening process exit signals.

We can dispose of the GraphQL Yoga instance when the server is closed like below.

import { createServer } from 'http'
import { createSchema, createYoga } from 'graphql-yoga'
 
const yoga = createYoga({
  schema: createSchema({
    typeDefs: /* GraphQL */ `
      type Query {
        hello: String
      }
    `,
    resolvers: {
      Query: { hello: () => 'world' }
    }
  })
})
 
const server = createServer(yoga)
server.listen(4000, () => {
  console.info('Server is running on http://localhost:4000/graphql')
})
server.once('close', async () => {
  await yoga.dispose()
  console.info('Server is disposed so is Yoga')
})

Plugin Disposal

If you have plugins that need some internal resources to be disposed of, you can use the onDispose hook to dispose of them. This hook will be invoked when the GraphQL Yoga instance is disposed like above.

let dbConnection: Connection
const plugin = {
  onPluginInit: async () => {
    dbConnection = await createConnection()
  },
  onDispose: async () => {
    // Dispose of resources
    await dbConnection.close()
  }
}

Or you can flush a queue of operations when the server is shutting down.

const backgroundJobs: Promise<void>[] = []
 
const plugin = {
  onRequestParse() {
    backgroundJobs.push(
      sendAnalytics({
        /* ... */
      })
    )
  },
  onDispose: async () => {
    // Flush the queue of background jobs
    await Promise.all(backgroundJobs)
  }
}

But for this kind of purposes, waitUntil can be a better choice.

Background jobs

If you have background jobs that need to be completed before the environment is shut down. waitUntil is better choice than onDispose. In this case, those jobs will keep running in the background but in case of disposal, they will be awaited. waitUntil works so similar to Cloudflare Workers’ waitUntil function.

But GraphQL Yoga handles waitUntil even if it is not provided by the environment.

const yoga = createSchema({
  typeDefs: /* GraphQL */ `
    type Query {
      greetings(name: String!): String
    }
  `,
  resolvers: {
    Query: {
      hello: (_, args, context) => {
        // This does not block the response
        context.waitUntil(
          fetch('http://my-analytics.com/analytics', {
            method: 'POST',
            body: JSON.stringify({
              name: args.name,
              userAgent: context.request.headers.get('User-Agent')
            })
          })
        )
        return `Hello, ${args.name}`
      }
    }
  }
})
 
const res = await yoga.fetch('http://localhost:4000/graphql', {
  query: /* GraphQL */ `
    query {
      greetings(name: "John")
    }
  `
})
 
console.log(await res.json()) // { data: { greetings: "Hello, John" } }
 
await yoga.dispose()
// The fetch request for \`analytics\` will be awaited here