@uploadflow/express
v0.1.0
Published
Express middleware for the uploadflow file-upload pipeline (direct, presigned, linked uploads)
Downloads
73
Maintainers
Readme
@uploadflow/express
Express middleware for the uploadflow file-upload pipeline: pluggable storage (Azure Blob with real SAS / AWS S3 / local / custom), magic-byte validation, presigned direct-to-storage uploads, and record-linked uploads.
npm i @uploadflow/express @uploadflow/core
# storage driver (optional peer of core) — install the one you use:
npm i @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
# or: npm i @azure/storage-blobPeer dep: express (^4 or ^5). multer is bundled.
Setup
import express from 'express';
import { createUploadFlow } from '@uploadflow/express';
import { InMemoryAttachmentStore } from '@uploadflow/core';
const uploads = createUploadFlow({
storage: {
driver: 's3',
s3: { region, bucket, accessKeyId, secretAccessKey },
},
validation: { maxFileSize: '5mb', allow: ['image/png', 'image/jpeg'], magicBytes: true },
urlMode: 'signed',
attachmentStore: new InMemoryAttachmentStore(), // or MongooseAttachmentStore.forRoot({...})
});
const app = express();
app.use(express.json());Direct upload
app.post('/avatar', ...uploads.single({ field: 'avatar' }), (req, res) => {
res.json(req.uploadedFile); // { key, url, size, mime, originalName }
});
app.post('/photos', ...uploads.array({ field: 'photos', maxCount: 5 }), (req, res) => {
res.json(req.uploadedFiles); // StoredObject[]
});single() / array() options: { field, allow?, maxFileSize?, category?, maxCount? }. Each returns an
array of middleware — spread it into the route.
Presigned upload
app.post('/uploads/presign', uploads.presignHandler()); // -> { uploadUrl, key, method, headers, expiresAt }
app.post('/uploads/confirm', uploads.confirmHandler()); // { key } -> StoredObject// client
const { uploadUrl, key, headers } = await api.post('/uploads/presign',
{ filename: file.name, contentType: file.type }).then(r => r.data);
await fetch(uploadUrl, { method: 'PUT', body: file, headers }); // straight to storage
const meta = await api.post('/uploads/confirm', { key }).then(r => r.data);Linked upload (attach to a record)
app.post('/media',
...uploads.linked({ fields: ['mediaFile'], module: 'MEDIA', recordIdFrom: (body) => body._id }),
(req, res) => {
// your handler creates the record and responds; uploadflow uploads + links, then injects
// `attachments: [...]` into the JSON before it's sent
res.json({ _id: 'rec1', title: req.body.title });
}
);linked() options: { fields, module?, allow?, maxFileSize?, recordIdFrom?, createdByFrom?, as? }.
Requires attachmentStore in createUploadFlow. On DELETE routes it unlinks by record id. On any
failure the uploaded blob is cleaned up (no orphans).
Error handling
Mount the error handler last so validation errors return the right status (400 / 413 / 404):
app.use(uploads.errorHandler());uploads.storage (a StorageService) and uploads.presign (a PresignService) are exposed for manual
use, e.g. await uploads.storage.resolveDownloadUrls(docs, { path: 'attachments[].key' }).
Full docs, config reference, urlMode, orphan handling, custom adapters: https://github.com/OWNER/uploadflow#readme
MIT
