graphql-upload
Express/Koa middleware and an Upload scalar for GraphQL multipart file uploads
Repository Health
Technical Analysis
graphql-upload is the server-side counterpart to clients like apollo-upload-client: it provides Express and Koa middleware that intercepts incoming GraphQL multipart requests (per the community graphql-multipart-request-spec), parses the multipart form to separate the GraphQL operations JSON from the uploaded file streams, and reassembles them into a single operations object where file variables become Upload scalar promises resolving to readable-stream-backed file objects.
It also exports the GraphQLUpload scalar type itself, so a schema can declare Upload as an input type and resolvers receive fully-formed file objects (filename, mimetype, encoding, and a createReadStream() method) instead of raw multipart parts, regardless of which Node.js GraphQL server framework or client sent the request.
What You Get
graphqlUploadExpress— Express middleware that detects and parses multipart GraphQL requests before they reach the GraphQL handlergraphqlUploadKoa— the equivalent middleware for Koa applicationsGraphQLUpload— a ready-to-use GraphQL scalar type for declaringUploadfields/arguments in a schemaprocessRequest— the underlying framework-agnostic request-processing function, usable directly for custom server integrationsUpload— the class instances resolvers receive, exposing filename/mimetype/encoding plus acreateReadStream()method for streaming file contents to storage
Common Use Cases
- Accepting file uploads (avatars, documents, media) through a mutation on an Express- or Koa-based GraphQL server
- Streaming uploaded files directly to cloud storage (S3, GCS) via
createReadStream()without buffering the whole file in memory - Interoperating with any client implementing the graphql-multipart-request-spec, including apollo-upload-client
- Integrating GraphQL file uploads into a custom or less common Node.js server setup via the lower-level
processRequestfunction
Under The Hood
Architecture - processRequest.mjs (roughly 400+ lines) is the core: it uses the busboy streaming multipart parser to read the incoming request, extracts the operations JSON part and the map part (which links file field names to JSON-pointer paths inside operations), then walks the map to substitute each referenced path with a Upload instance whose internal promise resolves once the corresponding file part streams in — giving resolvers a value they can await (or access via GraphQLUpload’s parseValue) even though the underlying bytes may still be arriving. graphqlUploadExpress.mjs and graphqlUploadKoa.mjs are thin adapters that call processRequest with the right request/response objects for each framework’s middleware signature, only engaging when the request’s content-type is multipart. Tech Stack - Pure ESM (.mjs, no CJS build), with busboy as the sole non-peer runtime dependency for streaming multipart parsing, and graphql, @types/express, @types/koa as (optional) peer dependencies so the package doesn’t force a specific server framework or pin a GraphQL version. Code Quality - Every module has an adjacent .test.mjs file, with processRequest.test.mjs alone exceeding 1,500 lines covering malformed multipart bodies, oversized files/field counts, aborted uploads, and both Express and Koa integration paths; // @ts-check JSDoc annotations provide type-checking without a separate TypeScript build. API Design - The layered design — a low-level processRequest plus framework-specific graphqlUploadExpress/graphqlUploadKoa wrappers — lets most consumers just drop in one middleware call, while server authors using an unsupported framework can still integrate via the documented lower-level function; the tradeoff is that Upload values are promises-in-disguise (resolved via a hidden .promise) which resolvers must await or otherwise handle correctly, a subtlety documented in the README but easy to miss on first use.
Used by 5 apps in this directory
AFFiNE
Productivity · Project Management · Note Taking
Write, draw, and plan in one infinite canvas — the open-source alternative to Notion and Miro that keeps your data yours.
Amplication
Developer Tools · AI Code Assistants · Automation
Create production-ready backend services with your organization's standards baked in — generating NestJS, Prisma, and GraphQL code that you own and control.
KeystoneJS
CMS · Developer Tools
The superpowered headless CMS for developers built with GraphQL and React
Parse Server
Developer Tools · Databases
Self-hosted Backend-as-a-Service for Node.js with REST, GraphQL, real-time Live Query, cloud code, and pluggable adapters for any infrastructure.
twenty
CRM
The open-source CRM you build, ship, and version like the rest of your stack — with customizable objects, AI agents, and a TypeScript SDK.