Skip to content

Commit 6d841c5

Browse files
committed
better error message for related errors
1 parent 68d9cd7 commit 6d841c5

File tree

3 files changed

+67
-32
lines changed

3 files changed

+67
-32
lines changed

.changeset/rude-squids-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'graphql-yoga': patch
3+
---
4+
5+
Improve error messages in case of `operatinName` related errors.

packages/graphql-yoga/src/plugins/request-validation/use-check-graphql-query-params.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { createGraphQLError } from '@graphql-tools/utils';
1+
import { Kind } from 'graphql';
2+
import { createGraphQLError, isDocumentNode } from '@graphql-tools/utils';
23
import type { GraphQLParams } from '../../types.js';
34
import type { Plugin } from '../types.js';
45

@@ -138,6 +139,55 @@ export function useCheckGraphQLQueryParams(extraParamNames?: string[]): Plugin {
138139
onParams({ params }) {
139140
checkGraphQLQueryParams(params, extraParamNames);
140141
},
142+
onParse() {
143+
return ({
144+
result,
145+
context: {
146+
request,
147+
params: { operationName },
148+
},
149+
}) => {
150+
// Run only if this is a Yoga request
151+
// the `request` might be missing when using graphql-ws for example
152+
// in which case throwing an error would abruptly close the socket
153+
if (!request || !isDocumentNode(result)) {
154+
return;
155+
}
156+
157+
let message: string | undefined;
158+
159+
const operations = result.definitions.filter(
160+
definition => definition.kind === Kind.OPERATION_DEFINITION,
161+
);
162+
163+
if (operationName) {
164+
const operationExists = operations.some(
165+
operation => operation.name?.value === operationName,
166+
);
167+
168+
if (!operationExists) {
169+
if (operations.length === 1) {
170+
message = `Operation name "${operationName}" doesn't match the name defined in the query.`;
171+
} else {
172+
message = `Could not determine what operation to execute. There is no operation "${operationName}" in the query.`;
173+
}
174+
}
175+
} else if (operations.length > 1) {
176+
message =
177+
'Could not determine what operation to execute. The query contains multiple operations, an operation name must be provided';
178+
}
179+
180+
if (message) {
181+
throw createGraphQLError(message, {
182+
extensions: {
183+
http: {
184+
status: 400,
185+
},
186+
},
187+
});
188+
}
189+
};
190+
},
141191
};
142192
}
143193

packages/graphql-yoga/src/plugins/request-validation/use-prevent-mutation-via-get.ts

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
1-
import { DocumentNode, getOperationAST, GraphQLError, OperationDefinitionNode } from 'graphql';
1+
import { DocumentNode, GraphQLError, Kind } from 'graphql';
22
import { Maybe } from '@envelop/core';
3-
import { createGraphQLError } from '@graphql-tools/utils';
3+
import { createGraphQLError, isDocumentNode } from '@graphql-tools/utils';
44
import type { YogaInitialContext } from '../../types.js';
55
import type { Plugin } from '../types.js';
66

7-
export function assertMutationViaGet(
8-
method: string,
9-
document: Maybe<DocumentNode>,
10-
operationName?: string,
11-
) {
12-
const operation: OperationDefinitionNode | undefined = document
13-
? getOperationAST(document, operationName) ?? undefined
14-
: undefined;
7+
export function assertMutationViaGet(method: string, document: Maybe<DocumentNode>) {
8+
const isMutation =
9+
document?.definitions.find(def => def.kind === Kind.OPERATION_DEFINITION)?.operation ===
10+
'mutation';
1511

16-
if (!operation) {
17-
throw createGraphQLError('Could not determine what operation to execute.', {
18-
extensions: {
19-
http: {
20-
status: 400,
21-
},
22-
},
23-
});
24-
}
25-
26-
if (operation.operation === 'mutation' && method === 'GET') {
12+
if (isMutation && method === 'GET') {
2713
throw createGraphQLError('Can only perform a mutation operation from a POST request.', {
2814
extensions: {
2915
http: {
@@ -41,15 +27,7 @@ export function usePreventMutationViaGET(): Plugin<YogaInitialContext> {
4127
return {
4228
onParse() {
4329
// We should improve this by getting Yoga stuff from the hook params directly instead of the context
44-
return ({
45-
result,
46-
context: {
47-
request,
48-
// the `params` might be missing in cases where the user provided
49-
// malformed context to getEnveloped (like `yoga.getEnveloped({})`)
50-
params: { operationName } = {},
51-
},
52-
}) => {
30+
return ({ result, context: { request } }) => {
5331
// Run only if this is a Yoga request
5432
// the `request` might be missing when using graphql-ws for example
5533
// in which case throwing an error would abruptly close the socket
@@ -67,7 +45,9 @@ export function usePreventMutationViaGET(): Plugin<YogaInitialContext> {
6745
throw result;
6846
}
6947

70-
assertMutationViaGet(request.method, result, operationName);
48+
if (isDocumentNode(result)) {
49+
assertMutationViaGet(request.method, result);
50+
}
7151
};
7252
},
7353
};

0 commit comments

Comments
 (0)