@stack-upload-orchestrator/node
v0.1.2
Published
Node.js upload handler — multipart parsing, validation, chunked uploads, Express adapter
Maintainers
Readme
@stack-upload-orchestrator/node
Node.js upload handler — multipart parsing, validation, chunked uploads, and an Express adapter.
Installation
npm install @stack-upload-orchestrator/nodeQuick start
import http from 'node:http';
import { handleUpload } from '@stack-upload-orchestrator/node';
import { LocalStorage } from '@stack-upload-orchestrator/storage-local';
const storage = new LocalStorage({ destination: './uploads' });
http.createServer(async (req, res) => {
if (req.method === 'POST' && req.url === '/upload') {
const files = await handleUpload(req, {
storage,
validation: { maxFileSize: '10MB', allowedTypes: ['image/*'] },
});
res.end(JSON.stringify({ files }));
}
}).listen(3000);handleUpload
handleUpload(req: IncomingMessage, options: UploadHandlerOptions): Promise<UploadResult[]>Parses a multipart request using busboy, validates each file, streams it through the storage adapter, and resolves with the results.
UploadHandlerOptions
| Option | Type | Description |
|---|---|---|
| storage | NodeStorageAdapter | Where to persist files (required) |
| validation | ValidationOptions | Size, type, extension, and count rules |
| onBeforeUpload | (meta) => Promise<void> | Hook called before each file is stored. Throw to reject. |
| onUploadComplete | (result, meta) => Promise<void> | Hook called after each file is stored successfully. |
| onError | (error, meta) => void | Called when a single file fails. |
Express adapter
import express from 'express';
import { expressUpload } from '@stack-upload-orchestrator/node';
import { LocalStorage } from '@stack-upload-orchestrator/storage-local';
const app = express();
const storage = new LocalStorage({ destination: './uploads' });
app.post(
'/upload',
expressUpload({
storage,
validation: { maxFileSize: '50MB' },
}),
(req, res) => {
res.json({ files: req.uploadedFiles });
},
);The middleware attaches results to req.uploadedFiles: UploadResult[].
Chunked uploads
For large files, split the upload into chunks on the client and reassemble on the server.
Server
import { handleChunkUpload, MemoryChunkStore } from '@stack-upload-orchestrator/node';
import { LocalStorage } from '@stack-upload-orchestrator/storage-local';
const store = new MemoryChunkStore();
const storage = new LocalStorage({ destination: './uploads' });
// POST /upload/chunk
const result = await handleChunkUpload(req, {
store,
storage,
validation: { maxFileSize: '500MB' },
});
if (result.done) {
console.log('File assembled:', result.file);
}HandleChunkOptions
| Option | Type | Description |
|---|---|---|
| store | MemoryChunkStore | Tracks in-progress chunk sessions |
| storage | NodeStorageAdapter | Storage adapter used when all chunks arrive |
| validation | ValidationOptions | Applied to the final assembled file |
The request must include these headers or form fields:
| Field | Description |
|---|---|
| x-chunk-index | Zero-based chunk index |
| x-chunk-total | Total number of chunks |
| x-file-name | Original filename |
| x-file-size | Total file size in bytes |
| x-upload-id | Unique ID for this upload session |
MemoryChunkStore
Keeps chunk data in memory. Suitable for single-server deployments. For multi-instance setups, implement the ChunkStore interface backed by Redis or a shared filesystem.
const store = new MemoryChunkStore();NodeStorageAdapter interface
import type { NodeStorageAdapter } from '@stack-upload-orchestrator/node';
const adapter: NodeStorageAdapter = {
async upload(stream: Readable, meta: NodeFileMetadata): Promise<UploadResult> {
// stream the file somewhere and return the result
},
async delete(fileId: string): Promise<void> { /* optional */ },
};License
MIT
