Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Support Auth in Fastify GraphQL Server by mimicking Lambda Events and Context #8533

Merged
35 changes: 32 additions & 3 deletions packages/fastify/src/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
import fastifyUrlData from '@fastify/url-data'
import type { FastifyInstance, HookHandlerDoneFunction } from 'fastify'
import fastifyRawBody from 'fastify-raw-body'

import type { GraphQLYogaOptions } from '@redwoodjs/graphql-server'
import { createGraphQLYoga } from '@redwoodjs/graphql-server'
import type { GraphQLYogaOptions } from '@redwoodjs/graphql-server'

/**
* Encapsulates the routes
* Transform a Fastify Request to an event compatible with the RedwoodGraphQLContext's event
* which is based on the AWS Lambda event
*/
import { lambdaEventForFastifyRequest as transformToRedwoodGraphQLContextEvent } from './lambda/index'

/**
* Redwood GraphQL Server Fastify plugin based on GraphQL Yoga
*
* Important: Need to set DISABLE_CONTEXT_ISOLATION = 1 in environment variables
* so that global context is populated correctly and features such as authentication
* works properly.
*
* It is critical to set shouldUseLocalStorageContext correctly so that the `setContext` function
* in the `useRedwoodPopulateContext` plugin sets the global context correctly with any
* extended GraphQL context as is done with `useRedwoodAuthContext` that sets
* the `currentUser` in the context when used to authenticate a user.
*
* See: packages/graphql-server/src/globalContext.ts
*
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://www.fastify.io/docs/latest/Reference/Plugins/#plugin-options
* @param {GraphQLYogaOptions} options GraphQLYogaOptions options used to configure the GraphQL Yoga Server
*/
export async function redwoodFastifyGraphQLServer(
fastify: FastifyInstance,
options: GraphQLYogaOptions,
done: HookHandlerDoneFunction
) {
// These two plugins are needed to transform a Fastify Request to a Lambda event
// which is used by the RedwoodGraphQLContext and mimics the behavior of the
// api-server withFunction plugin
fastify.register(fastifyUrlData)
await fastify.register(fastifyRawBody)

try {
const { yoga } = createGraphQLYoga(options)

Expand All @@ -22,6 +49,8 @@ export async function redwoodFastifyGraphQLServer(
const response = await yoga.handleNodeRequest(req, {
req,
reply,
event: transformToRedwoodGraphQLContextEvent(req),
requestContext: {},
})

for (const [name, value] of response.headers) {
Expand Down
2 changes: 1 addition & 1 deletion packages/fastify/src/lambda/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export async function lambdaRequestHandler(
return requestHandler(req, reply, LAMBDA_FUNCTIONS[routeName])
}

function lambdaEventForFastifyRequest(
export function lambdaEventForFastifyRequest(
request: FastifyRequest
): APIGatewayProxyEvent {
return {
Expand Down
17 changes: 17 additions & 0 deletions packages/graphql-server/src/globalContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ export interface GlobalContext extends Record<string, unknown> {}
let GLOBAL_CONTEXT: GlobalContext = {}
let PER_REQUEST_CONTEXT: AsyncLocalStorage<Map<string, GlobalContext>>

/**
*
* You must have shouldUseLocalStorageContext return true
* when you're self-hosting RedwoodJS.
*
* It is critical to set this correctly so that the `setContext` function
* in the `useRedwoodPopulateContext` plugin sets the global context
* correctly with any extended GraphQL context as is done with
* `useRedwoodAuthContext` that sets the `currentUser` in the context when
* used to authenticate a user.
*
* This will ensure that the GraphQLHandler will use the per-request context.
*
* You do not need to use LocalStorageContext for AWS (Netlify/Vercel)
* because each Lambda request is handled individually.
*
*/
export const shouldUseLocalStorageContext = () =>
process.env.DISABLE_CONTEXT_ISOLATION !== '1'

Expand Down