Storage
Uploads, MIME guards & a built-in CDN
The storage block gives your API file handling out of the box: multipart uploads with size and MIME validation, and a built-in CDN that serves the stored files back with caching, range requests and ETags. No S3 wiring required to get started.
Uploaded files are tracked as records (so they participate in auth and relations), and the CDN layer streams them efficiently — including partial content for video and large downloads.
Upload limits & guards#
Define what may be uploaded and how large it can be. MIME allow/block lists are your first line of defence against unwanted file types.
1{2 "storage": {3 "enabled": true,4 "basePath": "./uploads",5 "maxFileSizeBytes": 10485760,6 "allowedMimeTypes": ["image/jpeg", "image/png", "image/webp", "application/pdf"],7 "cdn": {8 "enabled": true,9 "basePath": "/cdn",10 "cacheMaxAge": 86400,11 "enableRangeRequests": true,12 "enableEtag": true,13 "corsOrigins": ["https://app.acme.com"]14 },15 "formData": { "filesField": "files", "dataField": "data", "maxFiles": 5 }16 }17}enabledbooleanOptionalMount the storage routes and CDN.
falsebasePathstringOptionalRoute prefix / base directory for stored files (e.g. ./uploads).
maxFileSizeBytesnumberOptionalHard upper bound on a single upload, in bytes. e.g. 10485760 = 10 MB. Oversized uploads are rejected before they're written.
allowedMimeTypesstring[]OptionalAllow-list of accepted MIME types. When set, anything not on the list is rejected — the safest posture for user uploads.
Example: ["image/jpeg", "image/png", "image/webp"]
blockedMimeTypesstring[]OptionalDeny-list of explicitly forbidden MIME types, useful when you'd rather allow-by-default but block known-dangerous types.
CDN delivery#
Serve stored files back to clients with proper HTTP caching semantics. Range requests enable seekable media and resumable downloads; ETags enable conditional requests.
cdnobjectOptionalBuilt-in content-delivery configuration.
enabledbooleanOptionalServe files over the CDN routes.
basePathstringOptionalPublic path files are served under (e.g. /cdn).
cacheMaxAgenumberOptionalCache-Control max-age in seconds sent with served files.
enableRangeRequestsbooleanOptionalHonour HTTP Range — required for video scrubbing and resumable downloads.
enableEtagbooleanOptionalEmit ETags so browsers can revalidate cheaply with 304s.
corsOriginsstring[]OptionalOrigins permitted to fetch CDN assets cross-origin.
Form-data field mapping#
Multipart uploads carry the binary files in one field and a JSON metadata blob in another. These settings name those fields and cap how many files one request may carry.
1// multipart upload to the storage basePath; fields match formData config2const form = new FormData();3form.append("files", file); // filesField4form.append(5 "data",6 JSON.stringify({ original_name: file.name, mime_type: file.type }),7); // dataField8 9await fetch("/api/storage", { method: "POST", body: form });10// the stored file is then served back at <cdn.basePath>/<id>formDataobjectOptionalMultipart request shape.
filesFieldstringOptionalThe form field carrying the binary file(s) — commonly "files".
dataFieldstringOptionalThe form field carrying the JSON metadata — commonly "data".
maxFilesnumberOptionalMaximum number of files accepted in a single multipart request.
Under the hood — BunFileManager#
Disk I/O goes through a single BunFileManager built on Bun's native file APIs (Bun.file / Bun.write). It's more than read/write — it's the crash-safe storage primitive the uploads and CDN sit on.
typed readstext · json · buffer · bytes · streamOptionalreadFile returns the format you ask for, and getFileInfo surfaces size, MIME type, extension and timestamps — which is how the CDN sets content types and validates uploads.
streaminglarge filesOptionalwriteStream / readFileStream / copyFileStream move data in chunks rather than buffering whole files in memory, so large uploads and downloads (and CDN range responses) stay flat on memory.
atomic writescrash-safeOptionalatomicWrite / atomicJsonWrite write to a temp file and swap, safeFileUpdate wraps a read-modify-write with automatic rollback, and batchAtomicOperations groups several — so a crash mid-write never leaves a half-written file.
permissionsoctal + helpersOptionalsetPermissions and the makeReadable/Writable/ReadOnly helpers manage POSIX file modes, letting stored artifacts be locked down on disk.
Related sections