@simongrayman/edge-upload
v0.1.0
Published
Modern, type-safe, and edge-compatible file upload parser for Web Standards.
Maintainers
Readme
edge-upload 🚀
A modern, type-safe, and edge-compatible file upload parser for Web Standards.
Say goodbye to legacy multipart parsers like multer. edge-upload is built from the ground up to work seamlessly in Node.js, Next.js (App Router), Cloudflare Workers, Deno, Bun, and any environment that supports the standard Web Request and Response APIs.
✨ Features
- 🌍 Universal: Works everywhere (Node.js, Edge, Serverless).
- 🛡️ Type-Safe: First-class TypeScript support with optional Zod integration.
- 🔒 Secure by Default: Automatically generates secure filenames (UUID) to prevent Directory Traversal attacks.
- ⚡ Lightweight: Zero heavy dependencies, built on native Web
FormDataAPI. - 🎯 Developer Experience: Clean, Promise-based API with structured error handling.
📦 Installation
npm install edge-upload(Optional but highly recommended for type-safe validation)
npm install zod🚀 Quick Start
Basic Usage (No Validation)
import { parseUpload, UploadError } from 'edge-upload';
export async function POST(request: Request) {
try {
const result = await parseUpload(request, {
maxFileSize: '5MB',
maxFiles: 1,
allowedMimeTypes: ['image/jpeg', 'image/png']
});
const file = result.files['avatar']?.[0];
const username = result.fields['username'];
return new Response(`Hello ${username}, received ${file.name}`, { status: 200 });
} catch (error) {
if (error instanceof UploadError) {
return new Response(error.message, { status: error.statusCode });
}
return new Response('Internal Server Error', { status: 500 });
}
}Advanced Usage (with Zod Validation) 🌟
import { parseUpload, UploadError } from 'edge-upload';
import { z } from 'zod';
const uploadSchema = z.object({
avatar: z.custom<File>().refine(
(file) => file.size <= 5 * 1024 * 1024,
{ message: "Max file size is 5MB" }
).refine(
(file) => ["image/jpeg", "image/png"].includes(file.type),
{ message: "Only JPG and PNG are allowed" }
),
username: z.string().min(3).max(20),
});
export async function POST(request: Request) {
try {
// Result is fully type-safe based on your Zod schema!
const result = await parseUpload(request, { schema: uploadSchema });
// result.data.avatar is guaranteed to be a valid File
// result.data.username is guaranteed to be a string (3-20 chars)
return Response.json({ success: true, data: result.data });
} catch (error) {
if (error instanceof UploadError && error.code === 'VALIDATION_ERROR') {
return Response.json({ errors: error.details }, { status: 400 });
}
return Response.json({ error: 'Upload failed' }, { status: 500 });
}
}⚙️ API Reference
parseUpload(request: Request, options?: ParseOptions)
Options:
maxFileSize(string | number): Maximum allowed file size (e.g.,'5MB','100KB', or5242880in bytes).maxFiles(number): Maximum number of files allowed in a single request (default:Infinity).allowedMimeTypes(string[]): Array of allowed MIME types (e.g.,['image/png', 'application/pdf']).preserveFileName(boolean): Iftrue, keeps the original filename. Iffalse(default), generates a secure UUID filename.schema(Zod Schema): Optional Zod schema for type-safe validation of both files and text fields.
Returns:
A ParseResult object containing:
files:Record<string, File[]>- Parsed files grouped by their form field name.fields:Record<string, string>- Parsed text fields.data:T- The fully validated data object (if a Zod schema was provided).
📄 License
MIT
