Skip to content

Defer doesn't work with multipart/mixed or text/event-stream #2069

Open
@fredericbirke

Description

@fredericbirke

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/[email protected]
├── @angular/[email protected]
├── @apollo/[email protected]
├── [email protected]
├── [email protected]
└── [email protected]
    "@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

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions