Skip to content

Commit

Permalink
feature: Support Auth in Fastify GraphQL Server by mimicking Lambda E…
Browse files Browse the repository at this point in the history
…vents and Context (redwoodjs#8533)

* WIP to see if graphql auth can work

* Remove debugs statements

* refactoring and documenting

* Small refactor

* plugins are needed to transform a Fastify Request to a Lambda event

* Remove unhelpful casts
  • Loading branch information
dthyresson authored Jun 8, 2023
1 parent 9ba397b commit f5c139d
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
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

0 comments on commit f5c139d

Please sign in to comment.