Documentation
Basics
Context

context

You might have noticed that in our load and resolve functions that we don't have the request parameters readily available to us, we might however need them to check i.e. if a user is authorized, ... This is done through the context object which will be carried around throughout the whole request lifecycle.

API-Route

By default we will expose params and headers on the context object. If you want to expose more data you can do so by adding it to the function invocation of your API handler.

import { createAPIRouteHandler } from 'fuse/next'
 
createAPIRouteHandler<{ userAgent: string }>({
  context: (ctx) => {
    return {
      userAgent: ctx.headers.get('user-agent') || 'unknown',
    }
  }
})

Now the userAgent will be available to all of our GraphQL resolvers!

Server components

In server-components we don't have the headers available by default when using the execute function, this to avoid automatically opting people into dynamic functions (opens in a new tab). We'll have to pass these in with the second argument of our execute function.

import { headers } from 'next/headers'
import { createAPIRouteHandler } from '@/fuse/server'
 
execute({
  query: x,
  variables: {},
  context: () => {
    return {
      userAgent: headers().get('user-agent') || 'unknown'
    }
  } 
})

This means that if you use context.headers or a related property in your resolvers that you will need to define this yourself if your executed document would tap into those resolvers.

Using context in your API

In our fields there are two opportunities we have to use context the first being during resolve and the second during load, below you can find an example of both.

import { node, addQueryFields, AuthenticationError } from 'fuse'
 
const UserNode = node<UserSource>({
  name: 'User',
  load: async (ids, ctx) => ctx.isAdmin ? getUsers(ids) : [],
  fields: (t) => ({
    name: t.exposeString('name'),
    // rename to camel-case
    avatarUrl: t.exposeString('avatar_url'),
    // Add an additional firstName field
    firstName: t.string({
      resolve: (user) => user.name.split(' ')[0],
    }),
  }),
})
 
addQueryFields((t) => ({
  me: t.list({
    type: UserNode,
    nullable: false,
    args: {
      offset: t.arg.int({}),
      limit: t.arg.int(),
      filter: t.arg({ type: FilterInput }),
    },
    resolve: async (_, args, context) => {
      if (context.userId) {
        return context.userId
      }
 
      throw new AuthenticationError('You must be logged in.')
    },
  }),
}))