GraphQL File Upload | Cosmo by WunderGraph - WunderGraph
GraphQL File Upload for Federated APIs
Clients send files alongside their GraphQL mutation. The router routes them to the right subgraph automatically.
One GraphQL endpoint. One auth layer. No encoding bloat.
Multipart request flow
- Client multipart/form-data operations · map · files
- Cosmo Router parse · validate · route
- Subgraph Upload scalar
The problem
Uploads should not live outside your graph
Federated teams still need files: avatars, attachments, imports. The wrong defaults split your API and punish bandwidth.
A separate REST endpoint fragments the API
When uploads bypass GraphQL, teams often reimplement authentication, rate limiting, and logging on a second path. Errors and traces follow different shapes than the rest of the API, and clients carry an extra integration for the same product.
Base64 over JSON wastes bandwidth
Putting binary data inside JSON usually means base64, which grows the payload by roughly a third compared to the raw file. Parsing that JSON costs more CPU on both sides, and the extra bytes add up across large files and repeated attempts.
Custom upload schemes don't travel
If each team invents its own envelope, every client library, SDK, and piece of tooling has to learn each one. Standard clients (Apollo, Relay, urql) already speak the multipart spec.
Our solution
Accept file uploads through your GraphQL API with no second endpoint
Cosmo Router implements the GraphQL multipart request specification. Clients send a multipart/form-data request with three parts: operations, map, and file parts. Declare an Upload scalar in your schema and you can accept files from any spec-compliant client.
The three parts
- operations: the GraphQL query with file variables set to
null - map: associates file fields to variable paths
- file parts: the actual bytes
At request time
Instead of splitting uploads onto a separate REST service, callers use the same federated GraphQL URL they already use for reads and mutations. The router accepts a multipart body, interprets it according to the spec, and continues the request as normal GraphQL work.
The router parses the payload, validates the mapping, and attaches each file to the correct variable before federation routes the mutation.
Declare an
Uploadscalar in your schema and implement it in the resolver layer: any spec-compliant client can send files through the same GraphQL endpoint as everything else.File size and file count limits are configured on the router in
config.yaml; malformed multipart requests are rejected at the router.
One endpoint. Standard tooling. Binary on the wire.
Before & After
| Before Cosmo | With Cosmo |
|---|---|
| Separate REST endpoint for uploads | One GraphQL endpoint for everything |
| Base64 encoding with 33% overhead | Binary multipart transfer |
| Custom routing logic for uploads | Automatic federation routing |
| Bespoke upload envelopes per client | Standard spec any major client speaks |
Router behavior
Technical behavior
- The router parses multipart payloads per spec and rejects malformed ones.
- File size and file count limits are configured on the router in
config.yaml. - Files are forwarded to the subgraph that resolves the mutation.
- Works with Apollo Client, Relay, urql, and other spec-compliant clients out of the box.
Requirements
- Declare
scalar Uploadin the subgraph schema and implement it in resolvers. - Use a multipart-capable GraphQL client.
How multipart upload works
One POST to your GraphQL endpoint
Clients keep the same gateway URL; the body is multipart instead of JSON-only, using the shape common GraphQL clients already support.
Malformed uploads rejected early.
The router parses the payload, reads the three parts, validates the mapping, and attaches each file to the correct variable.
Automatic federation routing
The router forwards to the subgraph that owns the mutation.
Binary: no base64 tax.
The subgraph stores or processes the file; the resolver writes to S3, disk, or another service and returns the mutation result.
Schema examples
Declare Upload once
Avatar, multi-file tickets, or bulk import: same scalar pattern.
Example schema
# GraphQL schema with file upload
scalar Upload
Limits in config
Cap upload size and file count at the router. See the docs for all options.
Upload through GraphQL today
Multipart spec, one router endpoint, configurable limits.
FAQ
GraphQL file upload
- What GraphQL file upload spec does Cosmo Router implement?
- Can I upload multiple files in a single GraphQL mutation?
- Which GraphQL clients support multipart file uploads?
- How do I limit GraphQL upload file size and count?
- What do I need to change in my subgraph to accept GraphQL file uploads?
- Is GraphQL multipart upload faster than base64 encoding?
More detail in the file upload documentation.