Skip to content

Conversation

@MaxGhenis
Copy link

@MaxGhenis MaxGhenis commented Jan 4, 2026

Summary

Fixes opencollective/opencollective#8458
Fixes opencollective/opencollective#8456

The GraphQL API proxy routes (pages/api/graphql/v1.js and v2.js) were using JSON.stringify(req.body) which corrupts multipart form data for file uploads via the useGraphQLFileUploader hook.

This also resolves the API issue opencollective/opencollective#8456 - while the direct API endpoint (api.opencollective.com) still has infrastructure issues with multipart, the frontend Apollo client uses the proxy path (/api/graphql/v2) which now works correctly.

Changes

  • Disable Next.js body parsing (bodyParser: false) to get raw request body
  • Detect multipart requests via Content-Type header
  • Forward multipart bodies as raw Buffer instead of JSON stringified
  • Keep JSON request handling unchanged (parse and re-stringify)
  • Add tests for both v1.js and v2.js (TDD - tests fail without fix, pass with fix)

Test plan

  • Added unit tests for v1.js multipart and JSON handling
  • Added unit tests for v2.js multipart and JSON handling
  • Tests verified to fail without fix (TDD red phase)
  • Tests pass with fix (TDD green phase)
  • Manual testing with file upload in development

Related

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Jan 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
opencollective-frontend Ready Ready Preview, Comment Jan 4, 2026 10:40pm

The GraphQL API proxy routes were using JSON.stringify(req.body) which
corrupts multipart form data for file uploads.

Changes:
- Disable Next.js body parsing to get raw request body
- Detect multipart requests via Content-Type header
- Forward multipart bodies as raw Buffer, not JSON stringified
- Keep JSON request handling unchanged

Fixes #11772

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Comment on lines 61 to 63
const rawBody = await getRawBody(req);
const jsonBody = JSON.parse(rawBody.toString() || '{}');
body = JSON.stringify(jsonBody);

This comment was marked as outdated.

Address Sentry review feedback: JSON.parse() without try/catch causes
unhandled errors when clients send malformed JSON. Now returns a proper
400 Bad Request with GraphQL-formatted error response instead of crashing.

Added test cases for both v1 and v2 proxies to verify behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@MaxGhenis
Copy link
Author

Sentry Review Addressed

Added error handling for JSON.parse() in both v1.js and v2.js proxies (commit 63cd485). Malformed JSON now returns 400 Bad Request with a GraphQL-formatted error instead of crashing with 500.

Manual Testing Note

The unit tests comprehensively cover:

  • ✅ Multipart requests forwarded as raw Buffer (not JSON stringified)
  • ✅ JSON requests parsed and re-stringified correctly
  • ✅ Malformed JSON returns 400 with proper error message
  • ✅ Content-Type headers preserved with boundary

Full manual testing with actual file upload requires a valid staging account. The dev server starts successfully and the API proxy routes are accessible at /api/graphql/v1 and /api/graphql/v2.

Comment on lines +24 to +30
async function getRawBody(req) {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}

This comment was marked as outdated.

Address Sentry review feedback: getRawBody() did not handle stream errors.
If a client disconnects during a file upload, the stream emits an error
that was previously unhandled, causing the server to crash.

Now wraps the body reading in try/catch and returns a proper 500 response
with GraphQL-formatted error message.

Added test cases for both v1 and v2 proxies to verify behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@MaxGhenis
Copy link
Author

Second Sentry Review Addressed

Added stream error handling (commit 0fbf2b3):

  • getRawBody() now catches stream errors (e.g., client disconnect during upload)
  • Returns 500 with GraphQL-formatted error instead of crashing
  • Added tests for both v1 and v2 proxies

All Sentry feedback now addressed:

  1. JSON.parse() error handling (400 for malformed JSON)
  2. ✅ Stream error handling (500 for client disconnect)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GraphQL proxy doesn't handle multipart file uploads GraphQL uploadFile mutation returns 500 error for OAuth API clients

1 participant