From 224088ac3ee54df393dffee5312831e999b8718e Mon Sep 17 00:00:00 2001 From: GitVasiliy Date: Tue, 17 Mar 2026 14:32:22 +0100 Subject: [PATCH] fix: pass operation to getEndpoint and getFetchOptions in doGraphqlRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In doGraphqlRequest, the `operation` variable was correctly extracted from `body.operation` (line 46) but `null` was passed to both `getEndpoint()` and `getFetchOptions()` instead of the actual value. This meant that `serverFetchOptions` and `graphqlEndpoint` callbacks always received `null` as the `operation` parameter, making it impossible to distinguish between queries and mutations in these hooks. The bug was introduced in commit 3c03c29 (initial file creation) where all four callsites used `null`. Commit 648d472 fixed `onServerResponse` and `onServerError` but missed `getEndpoint` and `getFetchOptions`. Note: the upload handler (upload.ts) was not affected — it calls getEndpoint/getFetchOptions directly and already passes `operation` correctly. Also fixes a minor inconsistency where `getFetchOptions` used `body.operationName || null` instead of the already-extracted `operationName` local variable. --- src/runtime/server/utils/doGraphqlRequest.ts | 6 +- .../serverHandler/doGraphqlRequest.test.ts | 155 ++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 test/runtime/serverHandler/doGraphqlRequest.test.ts diff --git a/src/runtime/server/utils/doGraphqlRequest.ts b/src/runtime/server/utils/doGraphqlRequest.ts index 5f40c9c..4af895a 100644 --- a/src/runtime/server/utils/doGraphqlRequest.ts +++ b/src/runtime/server/utils/doGraphqlRequest.ts @@ -54,7 +54,7 @@ export async function doGraphqlRequest( runtimeConfig, serverOptions, event, - null, + operation, operationName, context || null, ) @@ -63,8 +63,8 @@ export async function doGraphqlRequest( const fetchOptions = await getFetchOptions( serverOptions, event, - null, - body.operationName || null, + operation, + operationName, context, ) diff --git a/test/runtime/serverHandler/doGraphqlRequest.test.ts b/test/runtime/serverHandler/doGraphqlRequest.test.ts new file mode 100644 index 0000000..e2a35c2 --- /dev/null +++ b/test/runtime/serverHandler/doGraphqlRequest.test.ts @@ -0,0 +1,155 @@ +import type { IncomingMessage, ServerResponse } from 'node:http' +import { describe, expect, test, vi } from 'vitest' +import { H3Event } from 'h3' +import { GraphqlMiddlewareOperation } from './../../../src/runtime/settings' + +vi.mock('#nuxt-graphql-middleware/documents', () => { + return { + documents: { + query: { + testQuery: 'query TestQuery { test }', + }, + mutation: { + testMutation: 'mutation TestMutation { test }', + }, + }, + } +}) + +vi.mock('#nuxt-graphql-middleware/operation-variables', () => { + return { + operationVariables: {}, + } +}) + +vi.mock('#imports', () => { + return { + useRuntimeConfig: () => { + return { + public: { + 'nuxt-graphql-middleware': { + serverApiPrefix: '/nuxt-graphql-middleware', + }, + }, + graphqlMiddleware: { + graphqlEndpoint: 'http://localhost/graphql', + }, + } + }, + } +}) + +vi.mock('nitropack/runtime', () => { + return { + useEvent: () => null, + } +}) + +// Track what arguments serverFetchOptions and graphqlEndpoint receive. +const serverFetchOptionsCalls: Array<{ + operation: any + operationName: any +}> = [] + +const graphqlEndpointCalls: Array<{ + operation: any + operationName: any +}> = [] + +vi.mock('#nuxt-graphql-middleware/server-options', () => { + return { + serverOptions: { + serverFetchOptions: ( + _event: any, + operation: any, + operationName: any, + ) => { + serverFetchOptionsCalls.push({ operation, operationName }) + return {} + }, + graphqlEndpoint: ( + _event: any, + operation: any, + operationName: any, + ) => { + graphqlEndpointCalls.push({ operation, operationName }) + return 'http://localhost/graphql' + }, + }, + } +}) + +const fetchMock = { + raw: () => + Promise.resolve({ + _data: { data: {} }, + }), +} + +vi.stubGlobal('$fetch', fetchMock) + +function createEvent(): H3Event { + const req: Partial = { method: 'GET', url: '/' } + const res: Partial = {} + return new H3Event(req as any, res as any) +} + +describe('doGraphqlRequest', () => { + test('passes operation to serverFetchOptions and graphqlEndpoint for queries', async () => { + serverFetchOptionsCalls.length = 0 + graphqlEndpointCalls.length = 0 + + const { doGraphqlRequest } = await import( + './../../../src/runtime/server/utils/doGraphqlRequest' + ) + + await doGraphqlRequest( + { + query: 'query TestQuery { test }', + variables: {}, + operation: GraphqlMiddlewareOperation.Query, + operationName: 'testQuery', + }, + null, + createEvent(), + ) + + // serverFetchOptions should receive the operation, not null. + expect(serverFetchOptionsCalls).toHaveLength(1) + expect(serverFetchOptionsCalls[0].operation).toBe('query') + expect(serverFetchOptionsCalls[0].operationName).toBe('testQuery') + + // graphqlEndpoint should receive the operation, not null. + expect(graphqlEndpointCalls).toHaveLength(1) + expect(graphqlEndpointCalls[0].operation).toBe('query') + expect(graphqlEndpointCalls[0].operationName).toBe('testQuery') + }) + + test('passes operation to serverFetchOptions and graphqlEndpoint for mutations', async () => { + serverFetchOptionsCalls.length = 0 + graphqlEndpointCalls.length = 0 + + const { doGraphqlRequest } = await import( + './../../../src/runtime/server/utils/doGraphqlRequest' + ) + + await doGraphqlRequest( + { + query: 'mutation TestMutation { test }', + variables: {}, + operation: GraphqlMiddlewareOperation.Mutation, + operationName: 'testMutation', + }, + null, + createEvent(), + ) + + expect(serverFetchOptionsCalls).toHaveLength(1) + expect(serverFetchOptionsCalls[0].operation).toBe('mutation') + expect(serverFetchOptionsCalls[0].operationName).toBe('testMutation') + + expect(graphqlEndpointCalls).toHaveLength(1) + expect(graphqlEndpointCalls[0].operation).toBe('mutation') + expect(graphqlEndpointCalls[0].operationName).toBe('testMutation') + }) +})