Skip to content

Commit 528941c

Browse files
authored
fix: prefer multipart/mixed content-type for defer/stream operations (#2676)
1 parent 7784a71 commit 528941c

File tree

4 files changed

+86
-3
lines changed

4 files changed

+86
-3
lines changed
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+
Prefer `content-type: multipart/mixed` over `content-type: text/event-stream` when the client sends `accept: text/event-stream, multipart/mixed`.

β€Žpackages/graphql-yoga/__tests__/graphql-sse.spec.tsβ€Ž

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createSchema, createYoga } from '../src/index.js'
1+
import { Repeater, createSchema, createYoga } from '../src/index.js'
22
import { createClient } from 'graphql-sse'
33

44
describe('GraphQL over SSE', () => {
@@ -182,6 +182,46 @@ describe('GraphQL over SSE', () => {
182182
"
183183
`)
184184
})
185+
186+
it('accept: application/graphql-response+json, application/json, multipart/mixed, text/event-stream', async () => {
187+
const yoga = createYoga({
188+
schema: createSchema({
189+
typeDefs: /* GraphQL */ `
190+
type Query {
191+
hi: [String]
192+
}
193+
194+
type Subscription {
195+
hi: String!
196+
}
197+
`,
198+
resolvers: {
199+
Query: {
200+
hi: () => 'hi',
201+
},
202+
Subscription: {
203+
hi: {
204+
subscribe: () => new Repeater((push) => push({ hi: 'hi' })),
205+
},
206+
},
207+
},
208+
}),
209+
plugins: [],
210+
})
211+
212+
const response = await yoga.fetch('http://yoga/graphql', {
213+
method: 'POST',
214+
headers: {
215+
'content-type': 'application/json',
216+
accept:
217+
'application/graphql-response+json, application/json, multipart/mixed, text/event-stream',
218+
},
219+
body: JSON.stringify({ query: 'subscription { hi }' }),
220+
})
221+
222+
expect(response.status).toEqual(200)
223+
expect(response.headers.get('content-type')).toEqual('text/event-stream')
224+
})
185225
})
186226

187227
it.todo('Single connections mode')

β€Žpackages/graphql-yoga/src/plugins/useResultProcessor.tsβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export function useResultProcessors(opts: SSEProcessorOptions): Plugin {
4343
const sse = getSSEProcessorConfig(opts)
4444
const defaultList = [sse, multipart, regular]
4545
const subscriptionList = [multipart, sse, regular]
46+
4647
return {
4748
onSubscribe({ args: { contextValue } }) {
4849
if (contextValue.request) {
@@ -61,8 +62,9 @@ export function useResultProcessors(opts: SSEProcessorOptions): Plugin {
6162
: defaultList
6263
const requestMediaTypes = getMediaTypesForRequestInOrder(request)
6364
const isAsyncIterableResult = isAsyncIterable(result)
64-
for (const requestMediaType of requestMediaTypes) {
65-
for (const resultProcessorConfig of processorConfigList) {
65+
66+
for (const resultProcessorConfig of processorConfigList) {
67+
for (const requestMediaType of requestMediaTypes) {
6668
if (isAsyncIterableResult && !resultProcessorConfig.asyncIterables) {
6769
continue
6870
}

β€Žpackages/plugins/defer-stream/__tests__/defer-stream.spec.tsβ€Ž

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,42 @@ describe('Defer/Stream', () => {
458458
'multipart/mixed; boundary="-"',
459459
)
460460
})
461+
462+
it('accept: application/graphql-response+json, application/json, text/event-stream, multipart/mixed', async () => {
463+
const yoga = createYoga({
464+
schema: createSchema({
465+
typeDefs: /* GraphQL */ `
466+
type Query {
467+
hi: [String]
468+
}
469+
`,
470+
resolvers: {
471+
Query: {
472+
hi: () =>
473+
new Repeater(async (push, stop) => {
474+
await push('A')
475+
stop()
476+
}),
477+
},
478+
},
479+
}),
480+
plugins: [useDeferStream()],
481+
})
482+
483+
const response = await yoga.fetch('http://yoga/graphql', {
484+
method: 'POST',
485+
headers: {
486+
'content-type': 'application/json',
487+
accept:
488+
'application/graphql-response+json, application/json, text/event-stream, multipart/mixed',
489+
},
490+
body: JSON.stringify({ query: '{ hi @stream }' }),
491+
})
492+
expect(response.status).toEqual(200)
493+
expect(response.headers.get('content-type')).toEqual(
494+
'multipart/mixed; boundary="-"',
495+
)
496+
})
461497
})
462498

463499
describe('@graphql-tools/buildHTTPExecutor', () => {

0 commit comments

Comments
 (0)