graphql-upload
raw JSON → 17.0.0 verified Sat Apr 25 auth: no javascript
Middleware and a GraphQL Upload scalar for handling file uploads via multipart requests in Node.js GraphQL servers (Express, Koa, Apollo). Version 17.0.0 is ESM-only, requires Node.js ^18.18.0 || ^20.9.0 || >=22.0.0, and graphql ^16.3.0. Key differentiators: implements the GraphQL multipart request spec, supports deduplication of files across variables, streams files to cloud storage or filesystem, and provides both middleware functions and a low-level processRequest function for custom integrations. Released actively with breaking changes in major versions.
Common errors
error Cannot find module 'graphql-upload' ↓
cause Using import from the package root (e.g., import { GraphQLUpload } from 'graphql-upload') which is not exported since v14.
fix
Use deep imports: import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'
error TypeError: graphqlUploadExpress is not a function ↓
cause Using named import instead of default import for the middleware module.
fix
Use default import: import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'
error Error: Cannot find module 'graphql-upload/GraphQLUpload.mjs' ↓
cause The package version is <14.0.0 where modules were in .js files.
fix
Update to a newer version (>=14) or use the correct extension: 'graphql-upload/GraphQLUpload.js' for older versions.
error TypeError: file.createReadStream is not a function ↓
cause Calling createReadStream on the file promise before awaiting it, or the promise failed.
fix
Await the file promise first: const { createReadStream } = await file;
Warnings
breaking Since v16, the package is ESM-only in .mjs files and no longer supports CommonJS require(). Deep imports must be used. ↓
fix Switch to import syntax and use deep imports like import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'.
breaking In v14, the main index module was removed. Imports from 'graphql-upload' no longer work. ↓
fix Use deep imports for each module (e.g., 'graphql-upload/GraphQLUpload.mjs').
deprecated GraphQLUpload type import path changed. Previously import type { GraphQLUpload } from 'graphql-upload/Upload.mjs' now must be from 'graphql-upload/GraphQLUpload.mjs'. ↓
fix Update type imports: import type { GraphQLUpload } from 'graphql-upload/GraphQLUpload.mjs'.
gotcha File upload promise must be awaited in resolvers, otherwise the server may send a response before the upload completes, causing client disconnection. ↓
fix Ensure to await the file promise and handle stream errors: const { createReadStream } = await file;
security The busboy dependency used for multipart parsing had a vulnerability (CVE-2022-24434) affecting versions <15.0.0 of graphql-upload. ↓
fix Upgrade to graphql-upload >=15.0.0 which uses busboy v1 with the fix.
breaking In v15, busboy updated to v1, which changed some error messages. Code relying on exact error strings may break. ↓
fix Update any error handling to match the new busboy error messages.
gotcha The process must have read and write access to os.tmpdir() and sufficient disk space for concurrent uploads. ↓
fix Ensure os.tmpdir() is writable and monitor disk space.
Install
npm install graphql-upload yarn add graphql-upload pnpm add graphql-upload Imports
- graphqlUploadExpress wrong
import { graphqlUploadExpress } from 'graphql-upload'correctimport graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs' - graphqlUploadKoa wrong
import { graphqlUploadKoa } from 'graphql-upload'correctimport graphqlUploadKoa from 'graphql-upload/graphqlUploadKoa.mjs' - GraphQLUpload wrong
import { GraphQLUpload } from 'graphql-upload'correctimport GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs' - processRequest wrong
const processRequest = require('graphql-upload/processRequest.mjs')correctimport processRequest from 'graphql-upload/processRequest.mjs' - FileUpload wrong
import { FileUpload } from 'graphql-upload'correctimport type { FileUpload } from 'graphql-upload/processRequest.mjs'
Quickstart
import express from 'express';
import { createHandler } from 'graphql-http/lib/use/express';
import { buildSchema } from 'graphql';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
const schema = buildSchema(`
scalar Upload
type File {
filename: String!
mimetype: String!
encoding: String!
}
type Mutation {
singleUpload(file: Upload!): File!
}
type Query {
_: Boolean
}
`);
const rootValue = {
singleUpload: async (parent, { file }) => {
const { filename, mimetype, encoding, createReadStream } = await file;
const stream = createReadStream();
// Process stream (e.g., save to disk or cloud)
// For demo, just discard the stream
stream.resume();
return { filename, mimetype, encoding };
}
};
const app = express();
app.use('/graphql', graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }));
app.all('/graphql', createHandler({ schema, rootValue }));
app.listen(4000, () => console.log('Server running on port 4000'));