graphql-tools icon indicating copy to clipboard operation
graphql-tools copied to clipboard

http executor should always throw GraphQLError upon non 2xx response

Open tian-sheng-low opened this issue 2 years ago • 8 comments

Issue workflow progress

Progress of the issue based on the Contributor Workflow

  • [ ] 1. The issue provides a reproduction available on Github, Stackblitz or CodeSandbox

    Make sure to fork this template and run yarn generate in the terminal.

    Please make sure the GraphQL Tools package versions under package.json matches yours.

  • [ ] 2. A failing test has been provided
  • [ ] 3. A local solution has been provided
  • [ ] 4. A pull request is pending review

Describe the bug

buildHTTPExecutor with retry option unset(null) is returning the non 2XX response as it is without converting it into a GraphQLError object. This cause the uses of delegateToSchema to return null value instead of a GraphQLError when subschema return 500 error. Currently, the non 2XX status response check is only done if retry is set, which I think is unnecessary. https://github.com/ardatan/graphql-tools/blob/8676f5456f13ed50478cb249cfb25e6483b12f4a/packages/executors/http/src/index.ts#L202-L205

To Reproduce Steps to reproduce the behavior:

    const executor = buildHTTPExecutor({
      fetch: (_url, init) => new Response(JSON.stringify({ data: init }), { status: 500 }),
    });
    const result = await executor({
      document: parse(/* GraphQL */ `
        query {
          hello
        }
      `),
    });

The current implementation result is

{
  data: {
    body: '{"query":"{\\n  hello\\n}"}',
    headers: {
      accept: 'application/graphql-response+json, application/json, multipart/mixed',
      'content-type': 'application/json',
    },
    method: 'POST',
  },
}

Expected behavior

I would expect it to be returning a GraphQLError by throwing an error upon non-2XX status response regardless of the retry option value. The error will then be caught by the existing catch clause. https://github.com/ardatan/graphql-tools/blob/8676f5456f13ed50478cb249cfb25e6483b12f4a/packages/executors/http/src/index.ts#L242-L322

Environment:

  • OS: MacOS Ventura 13.2.1
  • @graphql-tools/...: packages/executors/http/
  • Deno: v1.34.3

Additional context

If the issue is reasonable, I could help by opening a PR.

tian-sheng-low avatar Jun 22 '23 07:06 tian-sheng-low

If the issue is reasonable, I could help by opening a PR.

Thanks for reporting the issue! We'd love to accept a PR with a failing test!

ardatan avatar Jun 22 '23 09:06 ardatan

I think I misunderstood the issue in the beginning. I think you are missing the case that a response can have a valid result with data and/or errors with a status code. In that case, your PR breaks this behavior and returns an error no matter what the actual response contains.

ardatan avatar Jun 22 '23 10:06 ardatan

a response can have a valid result with data and/or errors with a status code. In that case, your PR breaks this behavior and returns an error no matter what the actual response contains.

I see, it makes sense. However, I think the current code will also break this behavior if buildHttpExecutor() is called by setting the retry to any number other than 0, am I right?

tian-sheng-low avatar Jun 22 '23 10:06 tian-sheng-low

The check for retry should check if it is greater than 0. You're right.

ardatan avatar Jun 22 '23 15:06 ardatan

Great. Also, I would say that the status code check should be verifying if it starts with 5 for http error instead of not starting with 2. Could you explain what does the Retry should respect HTTP Errors comment means? Does it mean that it should not retry if http error occurs?

https://github.com/ardatan/graphql-tools/blob/8676f5456f13ed50478cb249cfb25e6483b12f4a/packages/executors/http/src/index.ts#L202-L205

tian-sheng-low avatar Jun 22 '23 15:06 tian-sheng-low

Also, I would say that the status code check should be verifying if it starts with 5 for http error instead of not starting with 2.

I am not sure with that. It is up to user because user can still want to pass some GraphQL data. If you want to throw an error in case of 5xx, you can pass a fetch function that throws in a 5xx response.

Retry should respect HTTP Errors

It means the executor should retry if the status code is not 2xx.

ardatan avatar Jun 22 '23 16:06 ardatan

It is up to user because user can still want to pass some GraphQL data. If you want to throw an error in case of 5xx, you can pass a fetch function that throws in a 5xx response.

Yes, I totally agree with you here. Maybe I am missing something here, but the current implementation will throw an error and essentially return a GraphQLError when the status code is not 2xx after exhausting all the retry attempts. Does that not violate the idea of allowing GraphQL data to pass through regardless of the status code?

Maybe it should be checking if the retry attempts have been exhausted. If so, json parse and return the fetched result regardless of the status code.

tian-sheng-low avatar Jun 22 '23 16:06 tian-sheng-low

@ardatan

checking if the retry attempts have been exhausted. If so, json parse and return the fetched result regardless of the status code.

I have added a failing test to my PR about my statement above. https://github.com/ardatan/graphql-tools/pull/5372/files

Kindly check it out when you have time :)

tian-sheng-low avatar Jun 26 '23 01:06 tian-sheng-low

Closing this because it has becomes stale for some time.

tian-sheng-low avatar Aug 28 '24 01:08 tian-sheng-low