GraphQL in Production · Advanced GraphQL
Files and Binary Data in GraphQL
Why multipart uploads are discouraged and what to do instead.
The GraphQL multipart request spec exists, but uploading files through GraphQL servers is discouraged: it complicates CSRF protection, buffering and gateways. The common pattern is: a mutation returns a pre-signed upload URL, the client uploads directly to object storage, then a second mutation attaches the uploaded file. Return files as URLs (signed, short-lived for private content), never base64 blobs in responses.
Upload options
| Criterion | Multipart via GraphQL | Pre-signed URL flow | Base64 in JSON |
|---|---|---|---|
| Server load | High | None | Very high (+33% size) |
| CSRF exposure | Needs extra care | None | None |
| Large files | Poor | Excellent | Unusable |
| Gateway support | Inconsistent | Universal | Universal |
type Mutation {
createUpload(input: CreateUploadInput!): CreateUploadPayload! # returns signed URL + uploadId
attachAvatar(input: AttachAvatarInput!): AttachAvatarPayload! # uploadId → user.avatar
}
type CreateUploadPayload { uploadId: ID! url: String! fields: JSON expiresAt: DateTime! }
Common pitfalls
- Base64 file payloads.
- Multipart endpoints without CSRF protection.
- Long-lived public file URLs.
Key points
- Signed URL + attach mutation
- Files as URLs in responses
- Avoid base64
Further reading: Apollo: File uploads guidance
Practice questions
Why is multipart upload support in GraphQL servers a CSRF concern?
Answer
Multipart/form-data is a 'simple' content type browsers can send cross-site without preflight, so cookie-authenticated endpoints accepting it need explicit CSRF protection.
How should a GraphQL API return a private file?
Answer
As a short-lived signed URL field, generated after authorization, rather than embedding file bytes.
Open in the app to mark lessons complete, take notes and track your progress.