diff --git a/.changeset/lovely-schools-live.md b/.changeset/lovely-schools-live.md new file mode 100644 index 0000000000..0923aa03e3 --- /dev/null +++ b/.changeset/lovely-schools-live.md @@ -0,0 +1,5 @@ +--- +'graphql-yoga': patch +--- + +Handle errors thrown in onRequest correctly diff --git a/packages/graphql-yoga/src/plugins/plugins.test.ts b/packages/graphql-yoga/src/plugins/plugins.test.ts index ad58ad4563..438f27ee9e 100644 --- a/packages/graphql-yoga/src/plugins/plugins.test.ts +++ b/packages/graphql-yoga/src/plugins/plugins.test.ts @@ -1,5 +1,4 @@ import { AfterValidateHook } from '@envelop/core' - import { createGraphQLError } from '../error.js' import { createSchema } from '../schema.js' import { createYoga } from '../server.js' @@ -57,4 +56,41 @@ describe('Yoga Plugins', () => { ], }) }) + it('should process errors from onRequest hook correctly', async () => { + const expectedStatus = 321 + const yoga = createYoga({ + schema, + plugins: [ + { + onRequest() { + throw createGraphQLError('My Error', { + extensions: { + http: { + status: expectedStatus, + }, + }, + }) + }, + }, + ], + }) + const response = await yoga.fetch('http://localhost:3000/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: '{hello}', + }), + }) + expect(response.status).toBe(expectedStatus) + const body = await response.json() + expect(body).toMatchObject({ + errors: [ + { + message: 'My Error', + }, + ], + }) + }) }) diff --git a/packages/graphql-yoga/src/server.ts b/packages/graphql-yoga/src/server.ts index 529dc7e47f..b096b9e453 100644 --- a/packages/graphql-yoga/src/server.ts +++ b/packages/graphql-yoga/src/server.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ExecutionResult, parse, specifiedRules, validate } from 'graphql' import { envelop, GetEnvelopedFn, @@ -13,7 +12,7 @@ import { useValidationCache, ValidationCache } from '@envelop/validation-cache' import { normalizedExecutor } from '@graphql-tools/executor' import { createFetch } from '@whatwg-node/fetch' import { createServerAdapter, ServerAdapter } from '@whatwg-node/server' - +import { ExecutionResult, parse, specifiedRules, validate } from 'graphql' import { handleError } from './error.js' import { createLogger, LogLevel, YogaLogger } from './logger.js' import { isGETRequest, parseGETRequest } from './plugins/requestParser/GET.js' @@ -484,28 +483,27 @@ export class YogaServer< } async getResponse(request: Request, serverContext: TServerContext) { - const url = new URL(request.url, 'http://localhost') - for (const onRequestHook of this.onRequestHooks) { - let response: Response | undefined - await onRequestHook({ - request, - serverContext, - fetchAPI: this.fetchAPI, - url, - endResponse(newResponse) { - response = newResponse - }, - }) - if (response) { - return response - } - } - - let requestParser: RequestParser | undefined - const onRequestParseDoneList: OnRequestParseDoneHook[] = [] let result: ResultProcessorInput - try { + const url = new URL(request.url, 'http://localhost') + for (const onRequestHook of this.onRequestHooks) { + let response: Response | undefined + await onRequestHook({ + request, + serverContext, + fetchAPI: this.fetchAPI, + url, + endResponse(newResponse) { + response = newResponse + }, + }) + if (response) { + return response + } + } + + let requestParser: RequestParser | undefined + const onRequestParseDoneList: OnRequestParseDoneHook[] = [] for (const onRequestParse of this.onRequestParseHooks) { const onRequestParseResult = await onRequestParse({ request,