Skip to content

Commit d1ffdf7

Browse files
committed
fix(handler): Respond with error if GraphQL execution result is iterable
1 parent 4eed1d5 commit d1ffdf7

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

src/__tests__/handler.ts

+19
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,22 @@ it.each(['schema', 'context', 'onSubscribe', 'onOperation'])(
7474
expect(optionFn.mock.calls[0][0]?.context).toBe(context);
7575
},
7676
);
77+
78+
it('should respond with error if execution result is iterable', async () => {
79+
const server = startTServer({
80+
// @ts-expect-error live queries for example
81+
execute: () => {
82+
return {
83+
[Symbol.asyncIterator]() {
84+
return this;
85+
},
86+
};
87+
},
88+
});
89+
90+
const url = new URL(server.url);
91+
url.searchParams.set('query', '{ __typename }');
92+
const result = await fetch(url.toString());
93+
94+
expect(result.status).toBe(400);
95+
});

src/handler.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ import {
1717
GraphQLError,
1818
} from 'graphql';
1919
import { isResponse, Request, RequestParams, Response } from './common';
20-
import { areGraphQLErrors, isExecutionResult, isObject } from './utils';
20+
import {
21+
areGraphQLErrors,
22+
isAsyncIterable,
23+
isExecutionResult,
24+
isObject,
25+
} from './utils';
2126

2227
/**
2328
* A concrete GraphQL execution context value type.
@@ -453,6 +458,13 @@ export function createHandler<RawRequest = unknown, Context = unknown>(
453458
if (isResponse(maybeResponseOrResult)) return maybeResponseOrResult;
454459
else if (maybeResponseOrResult) result = maybeResponseOrResult;
455460

461+
if (isAsyncIterable(result)) {
462+
return makeResponse(
463+
new GraphQLError('Subscriptions are not supported'),
464+
acceptedMediaType,
465+
);
466+
}
467+
456468
return makeResponse(result, acceptedMediaType);
457469
};
458470
}

src/utils.ts

+7
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ export function isExecutionResult(val: unknown): val is ExecutionResult {
2828
isObject(val) && ('data' in val || 'errors' in val || 'extensions' in val)
2929
);
3030
}
31+
32+
/** @private */
33+
export function isAsyncIterable<T = unknown>(
34+
val: unknown,
35+
): val is AsyncIterable<T> {
36+
return typeof Object(val)[Symbol.asyncIterator] === 'function';
37+
}

0 commit comments

Comments
 (0)