diff --git a/src/index.test.ts b/src/index.test.ts index 10ff2980..48a2ffea 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -6,7 +6,9 @@ import { GraphQLNonNull, } from 'graphql' import { Hono } from 'hono' +import type { Context, Next } from 'hono' import { errorMessages, graphqlServer } from '.' +import type { RootResolver } from './index' // Do not show `console.error` messages jest.spyOn(console, 'error').mockImplementation() @@ -26,34 +28,32 @@ describe('errorMessages', () => { }) describe('GraphQL Middleware - Simple way', () => { - // Construct a schema, using GraphQL schema language - const schema = buildSchema(` - type Query { - hello: String - } -`) - - // The root provides a resolver function for each API endpoint - const rootValue = { - hello: () => 'Hello world!', - } - - const app = new Hono() - - app.use( - '/graphql', - graphqlServer({ - schema, - rootValue, - }) - ) - - app.all('*', (c) => { - c.header('foo', 'bar') - return c.text('fallback') - }) - it('Should return GraphQL response', async () => { + const schema = buildSchema(` + type Query { + hello: String + } + `) + + const rootValue = { + hello: () => 'Hello world!', + } + + const app = new Hono() + + app.use( + '/graphql', + graphqlServer({ + schema, + rootResolver: () => rootValue, + }) + ) + + app.all('*', (c) => { + c.header('foo', 'bar') + return c.text('fallback') + }) + const query = 'query { hello }' const body = { query: query, @@ -71,6 +71,59 @@ describe('GraphQL Middleware - Simple way', () => { expect(await res.text()).toBe('{"data":{"hello":"Hello world!"}}') expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler }) + + it('Should access the ctx', async () => { + const schema = buildSchema(` + type Query { + hi: String + } + `) + + const rootResolver: RootResolver = (ctx?: Context) => { + const name = ctx?.get('name') + return { + hi: `hi ${name}` + } + } + + const app = new Hono() + + app.use('*', async (ctx: Context, next: Next) => { + ctx.set('name', 'Jason') + await next() + }) + + + app.use( + '/graphql', + graphqlServer({ + schema, + rootResolver, + }) + ) + + app.all('*', (c) => { + c.header('foo', 'bar') + return c.text('fallback') + }) + + const query = 'query { hi }' + const body = { + query: query, + } + + const res = await app.request('http://localhost/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(await res.text()).toBe('{"data":{"hi":"hi Jason"}}') + expect(res.headers.get('foo')).toBeNull() // GraphQL Server middleware should be Handler + }) }) const QueryRootType = new GraphQLObjectType({ diff --git a/src/index.ts b/src/index.ts index 1dd6e385..61a6ff56 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,3 @@ -// Based on the code in the `express-graphql` package. -// https://github.com/graphql/express-graphql/blob/main/src/index.ts - import { Source, parse, @@ -21,12 +18,13 @@ import type { } from 'graphql' import type { Context } from 'hono' -import type { Next } from 'hono' import { parseBody } from './parse-body' +export type RootResolver = (ctx?: Context) => Promise | unknown + type Options = { schema: GraphQLSchema - rootValue?: unknown + rootResolver?: RootResolver, pretty?: boolean validationRules?: ReadonlyArray // graphiql?: boolean @@ -34,12 +32,11 @@ type Options = { export const graphqlServer = (options: Options) => { const schema = options.schema - const rootValue = options.rootValue const pretty = options.pretty ?? false const validationRules = options.validationRules ?? [] // const showGraphiQL = options.graphiql ?? false - return async (c: Context, next: Next) => { + return async (c: Context) => { // GraphQL HTTP only supports GET and POST methods. if (c.req.method !== 'GET' && c.req.method !== 'POST') { return c.json(errorMessages(['GraphQL only supports GET and POST requests.']), 405, { @@ -119,12 +116,13 @@ export const graphqlServer = (options: Options) => { } let result: FormattedExecutionResult + const { rootResolver } = options try { result = await execute({ schema, document: documentAST, - rootValue, + rootValue: rootResolver ? await rootResolver(c) : null, variableValues: variables, operationName: operationName, }) @@ -161,8 +159,6 @@ export const graphqlServer = (options: Options) => { } else { return c.json(result) } - - await next() // XXX } }