-
Notifications
You must be signed in to change notification settings - Fork 15
fix(gql): switch to using handleNodeRequestAndResponse
#957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
see migration notes here: graphql-hive/graphql-yoga#3215 (comment) and also the docs for useExecutionCancellation here: https://the-guild.dev/graphql/yoga-server/docs/features/execution-cancellation
✅ Deploy Preview for cedarjs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
Updated GraphQL Yoga handler to use handleNodeRequestAndResponse instead of handleNodeRequest, which is necessary for enabling execution cancellation support.
Critical Issue Found:
- The implementation incorrectly attempts to manually write the response after calling
handleNodeRequestAndResponse. According to the GraphQL Yoga v5 migration notes,handleNodeRequestAndResponseautomatically writes to the response object, so the manual header/status/body operations (lines 104-109) are redundant and will cause double-writing issues.
Additional Observation:
- The
useExecutionCancellationplugin mentioned in the PR description is not actually being added to the plugin list increateGraphQLYoga.ts. Simply changing the method name enables the capability, but the plugin should be explicitly added if execution cancellation is desired.
Confidence Score: 2/5
- This PR has a critical implementation bug that will cause runtime errors with double response writing
- The change from
handleNodeRequesttohandleNodeRequestAndResponseis correct in principle, but the implementation is fundamentally broken. The code still manually writes headers, status, and body after callinghandleNodeRequestAndResponse, which automatically handles this. This will likely cause errors like "Cannot set headers after they are sent" or response corruption. Additionally, theuseExecutionCancellationplugin is not being added anywhere. packages/api-server/src/plugins/graphql.tsrequires immediate correction to remove redundant response writing
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| packages/api-server/src/plugins/graphql.ts | 2/5 | Updated to handleNodeRequestAndResponse but incorrectly still manually writing response, which will cause double-writing issues |
Sequence Diagram
sequenceDiagram
participant Client
participant Fastify
participant GraphQLHandler
participant Yoga
participant Reply
Client->>Fastify: HTTP Request
Fastify->>GraphQLHandler: route handler(req, reply)
GraphQLHandler->>Yoga: handleNodeRequestAndResponse(req, reply, context)
Note over Yoga,Reply: Yoga writes response directly to reply object
Yoga->>Reply: Set headers, status, body
Yoga-->>GraphQLHandler: Returns Response object
Note over GraphQLHandler: ⚠️ ISSUE: Code tries to write again
GraphQLHandler->>Reply: reply.header() - redundant
GraphQLHandler->>Reply: reply.status() - redundant
GraphQLHandler->>Reply: reply.send() - redundant
GraphQLHandler-->>Fastify: return reply
Fastify-->>Client: HTTP Response
Additional Comments (1)
|
| const graphQLYogaHandler = async ( | ||
| req: FastifyRequest, | ||
| reply: FastifyReply, | ||
| ) => { | ||
| const response = await yoga.handleNodeRequest(req, { | ||
| const response = await yoga.handleNodeRequestAndResponse(req, reply, { | ||
| req, | ||
| reply, | ||
| event: lambdaEventForFastifyRequest(req), | ||
| requestContext: {}, | ||
| }) | ||
|
|
||
| for (const [name, value] of response.headers) { | ||
| reply.header(name, value) | ||
| } | ||
|
|
||
| reply.status(response.status) | ||
| reply.send(response.body) | ||
|
|
||
| return reply | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure if there are other implications of this change for cedar internals.
The hope is that this is a starting point for investigation
|
@cstoltze Thanks for getting started on this PR! Did you read Graphile's Summary? Is anything it says valid? |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run-many -t build:pack --exclude create-ceda... |
✅ Succeeded | 2s | View ↗ |
nx run-many -t test --minWorkers=1 --maxWorkers=4 |
✅ Succeeded | 2m 43s | View ↗ |
nx run-many -t test:types |
✅ Succeeded | 10s | View ↗ |
nx run-many -t build |
✅ Succeeded | 1m 31s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-01-09 20:16:58 UTC
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run-many -t build |
✅ Succeeded | 14s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-01-08 17:46:33 UTC
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run-many -t build |
✅ Succeeded | 3s | View ↗ |
nx run-many -t build:pack --exclude create-ceda... |
✅ Succeeded | 7s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-01-08 17:49:21 UTC
@Tobbe Thanks for taking a look at this! I'm no expert on cedar internals, so will defer to your judgement. I did read greptile's summary and I do not think it's raising legitimate concerns. The example fastify app for graphql yoga looks like this which seems identical to what is present in cedar. So I think this should work as-is. |
I think this might be a better example to look at If we wanted to enable request cancellation out of the box, we'd need to include that plugin. Another option would be to only switch to |
0f0e039 to
aa1c62d
Compare
|
Thanks for finding the better reference file!
This is a good point. I was definitely thinking more along the lines of using |
| for (const [name, value] of response.headers) { | ||
| reply.header(name, value) | ||
| } | ||
|
|
||
| reply.status(response.status) | ||
| reply.send(response.body) | ||
|
|
||
| return reply | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setting headers/status/body seemed unnecessary based on the example here: https://github.com/graphql-hive/graphql-yoga/blob/083d65163a0d4868f0547c06fa366da6b4013eaa/examples/fastify/src/app.ts#L121-L125
Great, because that's what I'd want. At least for now. I just wasn't sure with the title of the PR and the description. How about we merge this PR with the code changes like they are right now, and then after you guys have tried it out with |
Perfect! Yes, sorry for the imprecise naming.
This sounds good. Yes please rewrite the description/title of the PR to better reflect this. We will keep you updated on how this works when we try it out. We're currently upgrading to redwood latest and plan to migrate to cedar shortly afterwards. This functionality is one of many reasons we're interested in moving to cedar. Thanks again for looking at this! We really appreciate your assistance on this and your efforts on cedar more generally. |
handleNodeRequestAndResponse
|
@greptileai Please do a full review of this PR again and update your review Summary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
This PR switches from handleNodeRequest to handleNodeRequestAndResponse and removes the manual response handling code that was incorrectly duplicating what handleNodeRequestAndResponse already does internally.
Key Changes:
- Switches from
yoga.handleNodeRequest()toyoga.handleNodeRequestAndResponse() - Removes manual header copying, status setting, and body sending
- Simplifies the handler to an arrow function expression
Context:
This change enables GraphQL Yoga's execution cancellation feature (as noted in commit 94cb0f3). The handleNodeRequestAndResponse method automatically writes the response to the Fastify reply object, making the manual response handling in the old code redundant and potentially problematic (could cause double-write issues).
Migration Reference:
The implementation follows the GraphQL Yoga 5.x migration pattern documented in graphql-hive/graphql-yoga#3215 and matches their official Fastify example.
Confidence Score: 4/5
- Safe to merge - correctly implements GraphQL Yoga 5.x API with minor style improvement recommended
- The PR correctly switches to
handleNodeRequestAndResponseand removes the problematic manual response handling that would have caused issues. The implementation follows GraphQL Yoga's official migration pattern. The only concern is a minor style issue where the handler should explicitly returnreplyfollowing Fastify conventions, but this is non-blocking as the response is already sent internally. - No files require special attention - the single file change is straightforward and correct
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| packages/api-server/src/plugins/graphql.ts | 4/5 | Switches to handleNodeRequestAndResponse and removes manual response handling. The handler should explicitly return reply for proper Fastify integration. |
Sequence Diagram
sequenceDiagram
participant Client
participant Fastify
participant Handler as graphQLYogaHandler
participant Yoga as yoga.handleNodeRequestAndResponse
participant Reply as FastifyReply
Client->>Fastify: HTTP Request
Fastify->>Handler: route handler(req, reply)
Handler->>Yoga: handleNodeRequestAndResponse(req, reply, context)
Note over Yoga: Processes GraphQL request
Note over Yoga: Parses query
Note over Yoga: Executes resolvers
Note over Yoga: Formats response
Yoga->>Reply: Sets headers
Yoga->>Reply: Sets status code
Yoga->>Reply: Calls reply.send(body)
Yoga-->>Handler: Returns (reply already sent)
Handler-->>Fastify: Returns result
Fastify-->>Client: HTTP Response
| const graphQLYogaHandler = async ( | ||
| req: FastifyRequest, | ||
| reply: FastifyReply, | ||
| ) => { | ||
| const response = await yoga.handleNodeRequest(req, { | ||
| ) => | ||
| yoga.handleNodeRequestAndResponse(req, reply, { | ||
| req, | ||
| reply, | ||
| event: lambdaEventForFastifyRequest(req), | ||
| requestContext: {}, | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P3] While handleNodeRequestAndResponse writes the response internally, Fastify handlers should explicitly return reply when the reply has been sent. This ensures proper type checking and follows Fastify best practices.
| const graphQLYogaHandler = async ( | |
| req: FastifyRequest, | |
| reply: FastifyReply, | |
| ) => { | |
| const response = await yoga.handleNodeRequest(req, { | |
| ) => | |
| yoga.handleNodeRequestAndResponse(req, reply, { | |
| req, | |
| reply, | |
| event: lambdaEventForFastifyRequest(req), | |
| requestContext: {}, | |
| }) | |
| const graphQLYogaHandler = async ( | |
| req: FastifyRequest, | |
| reply: FastifyReply, | |
| ) => { | |
| await yoga.handleNodeRequestAndResponse(req, reply, { | |
| req, | |
| reply, | |
| event: lambdaEventForFastifyRequest(req), | |
| requestContext: {}, | |
| }) | |
| return reply | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/api-server/src/plugins/graphql.ts
Line: 93:102
Comment:
[P3] While `handleNodeRequestAndResponse` writes the response internally, Fastify handlers should explicitly return `reply` when the reply has been sent. This ensures proper type checking and follows Fastify best practices.
```suggestion
const graphQLYogaHandler = async (
req: FastifyRequest,
reply: FastifyReply,
) => {
await yoga.handleNodeRequestAndResponse(req, reply, {
req,
reply,
event: lambdaEventForFastifyRequest(req),
requestContext: {},
})
return reply
}
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.|
Looking at the example here https://the-guild.dev/graphql/yoga-server/docs/integrations/integration-with-fastify#example they're only passing yoga.handleNodeRequestAndResponse(req, reply, {
req,
reply
})It might be fine. It's what we did before too, when using |
|
I added types to the And now, with how simple the handler has become, I figured we might as well just inline it, which also makes it follow that same example much closer Assuming CI passes I'm going to merge this PR now despite still not fully understanding the extra |
|
I'm now more confident in the |

Update the graphql yoga handler to allow cedar users to enable the useExecutionCancellation plugin using
extraPlugins.Execution cancellation is useful to stop resolving data on the server when the client is no longer interested in the response. This PR sets up the graphql handler so that this plugin is available if cedar users want it.
See graphql-yoga migration notes for justification of why the handler needs to change in order for this plugin to function correctly.
handleNodeRequest()is also deprecated: