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

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

At request time

  1. 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.

  2. The router parses the payload, validates the mapping, and attaches each file to the correct variable before federation routes the mutation.

  3. Declare an Upload scalar 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.

  4. 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

Requirements

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

More detail in the file upload documentation.