the-guild-org / the-guild-org/apollo-angular
Defer doesn't work with multipart/mixed or text/event-stream
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.5k
- Forks
- 309
- Avg merge
- 1h 45m
- Merged PRs (30d)
- 3
Description
Describe the bug
Schema:
schema {
query: Query
}
type Query {
loadFoo: Foo!
}
type Foo {
name: String!
}
"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`."
directive @defer(
"Deferred when true."
if: Boolean,
"If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to."
label: String
) on FRAGMENT_SPREAD | INLINE_FRAGMENT
I use @graphql-codegen/typescript-apollo-angular for codegen.
When using this query:
query foos {
loadFoo {
... @defer {
name
}
}
}
, generate code (which works fine) and then try to run it, I get the following error:
ERROR ApolloError: Http failure during parsing for http://localhost:4200/graphql
at new ApolloError (index.js:28:28)
at QueryManager.js:673:19
at both (asyncMap.js:16:53)
at asyncMap.js:9:72
at new ZoneAwarePromise (zone.js:1411:21)
at Object.then (asyncMap.js:9:24)
at Object.error (asyncMap.js:18:26)
at notifySubscription (module.js:137:18)
at onNotify (module.js:176:3)
at SubscriptionObserver.error (module.js:229:5)
While digging deeper, you can find another error message:
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null
},
"status": 200,
"statusText": "OK",
"url": "http://localhost:4200/graphql",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure during parsing for http://localhost:4200/graphql",
"error": {
"error": {
"message": "No number after minus sign in JSON at position 3"
"stack: "SyntaxError: No number after minus sign in JSON at position 3
at JSON.parse (<anonymous>)
at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:22534:41)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:171)
at http://localhost:4200/vendor.js:34143:49
at AsyncStackTaggingZoneSpec.onInvokeTask (http://localhost:4200/vendor.js:34143:30)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:54)
at Object.onInvokeTask (http://localhost:4200/vendor.js:34454:25)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:54)
at Zone.runTask (http://localhost:4200/polyfills.js:8037:37)
at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:8312:26)"
},
"text": "\r\n---\r\nContent-Type: application/json; charset=utf-8\r\n\r\n{\"data\":{\"loadFoo\":{\"__typename\":\"Foo\"}},\"hasNext\":true}\r\n---\r\nContent-Type: application/json; charset=utf-8\r\n\r\n{\"incremental\":[{\"data\":{\"name\":\"Bar\",\"__typename\":\"Foo\"},\"path\":[\"loadFoo\"]}],\"hasNext\":false}\r\n-----\r\n"
}
}
Which might make sense, because the response is incremental. The Backends response looks like this (this is the multipart/mixed format):
---
Content-Type: application/json; charset=utf-8
{"data":{"loadFoo":{"__typename":"Foo"}},"hasNext":true}
---
Content-Type: application/json; charset=utf-8
{"incremental":[{"data":{"name":"Bar","__typename":"Foo"},"path":["loadFoo"]}],"hasNext":false}
-----
If I try to use text/event-stream the backend anwers like this:
event: next
data: {"data":{"sanctuaryById":{"kennels":[{"id":"ccce9657-fec8-4ce0-9d7e-0160e9cec514","name":"Test","__typename":"Kennel"}],"__typename":"Sanctuary"}},"hasNext":true}
event: next
data: {"incremental":[{"data":{"name":"Dingens","__typename":"Sanctuary"},"path":["sanctuaryById"]}],"hasNext":false}
event: complete
but then the Apollo Error looks like this:
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null
},
"status": 200,
"statusText": "OK",
"url": "http://localhost:4200/graphql",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure during parsing for http://localhost:4200/graphql",
"error": {
"error": {
"message": "Unexpected token 'e', "event: nex"... is not valid JSON"
"stack":"SyntaxError: Unexpected token 'e', "event: nex"... is not valid JSON
at JSON.parse (<anonymous>)
at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:22534:41)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:171)
at http://localhost:4200/vendor.js:34143:49
at AsyncStackTaggingZoneSpec.onInvokeTask (http://localhost:4200/vendor.js:34143:30)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:54)
at Object.onInvokeTask (http://localhost:4200/vendor.js:34454:25)
at _ZoneDelegate.invokeTask (http://localhost:4200/polyfills.js:8235:54)
at Zone.runTask (http://localhost:4200/polyfills.js:8037:37)
at ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.js:8312:26)"
},
"text": "event: next\ndata: {\"data\":{\"loadFoo\":{\"__typename\":\"Foo\"}},\"hasNext\":true}\n\nevent: next\ndata: {\"incremental\":[{\"data\":{\"name\":\"Bar\",\"__typename\":\"Foo\"},\"path\":[\"loadFoo\"]}],\"hasNext\":false}\n\nevent: complete\n\n"
}
}
I tested this with the react-client and it simply works. Maybe it's a bug in the codegen? I didn't use Codegen in the react test.
To Reproduce
Create a simple Backend with HotChocolate https://chillicream.com/docs/hotchocolate/v13 and defer any field. It doesn't work with the simplest case. If you want I can provide you one.
Expected behavior
Defer should work.
Environment:
├── @angular/cli@16.2.1
├── @angular/core@16.2.3
├── @apollo/client@3.8.2
├── apollo-angular@5.0.1
├── graphql@16.8.0
└── typescript@5.1.3
"@graphql-codegen/add": "^5.0.0",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript-apollo-angular": "^3.5.6",
"@graphql-codegen/typescript-operations": "^4.0.1",
Additional context
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the issue with the listed Angular, Apollo Client, and GraphQL versions, using the Hot Chocolate defer example and comparing it with the working React client. Inspect how the Angular client handles multipart/mixed and text/event-stream responses; done means deferred responses are consumed without JSON parsing errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular, graphql, typescript
- Domain
- api, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100