@asterflow/multipart
v2.2.0
Published
Parses multipart/form-data requests before your handler runs, with optional per-route field rules checked at runtime and enforced in the handler's types.
Maintainers
Readme
@asterflow/multipart
Parses
multipart/form-datarequests before your handler runs, with optional per-route field rules that are checked at runtime and enforced in the handler's types.
📦 Installation
bun install @asterflow/multipartRegister the plugin on an AsterFlow app:
import { AsterFlow } from 'asterflow'
import { multipartPlugin } from '@asterflow/multipart'
const app = new AsterFlow()
.use(multipartPlugin, { limits: { fileSize: 10 * 1024 * 1024 } })✨ Features
- Automatic parsing - any
multipart/form-datarequest is parsed withbusboybefore it reaches your route. - Per-route criteria - calling
.multipart({...})onMethod.create(...)(or inside aRouter.builder(...).method(...)chain) validates fields at runtime and narrowsgetFile/getFilesin that handler's types - arequiredfield types as always-present, a declaredmimeTypeslist narrows.mimeType. - Request extensions -
request.body,request.files,request.getFile(),request.getFiles(),request.hasFiles(),request.getFilesByType(),request.saveAll()andrequest.cleanupMultipart()are attached directly ontorequest, no wrapper object. - Streaming storage - files stream into memory or to disk (
fileHandling.keepInMemory), never buffered twice. - Automatic cleanup - temp files written to disk are removed after the response is sent.
- Standardized errors - limit, MIME/extension and required-field failures all reject with the same
{ error, code, message }shape before your handler runs.
❓ How to Use
Declare a route's fields with .multipart(schema) - the handler only runs once the request passes that schema, and getFile/getFiles are typed to match it:
import { Method } from '@asterflow/router'
export default Method.create(Method.POST, { path: '/avatar' })
.multipart({
avatar: { mimeTypes: ['image/png', 'image/jpeg'], maxSize: 5 * 1024 * 1024, required: true }
})
.handler(({ request, response }) => {
const avatar = request.getFile('avatar') // always present, mimeType narrowed
return response.success({ filename: avatar.filename, size: avatar.size })
})Without a declared schema, the same methods are still there on request, just optional and unnarrowed:
export default Method.create(Method.POST, { path: '/upload' }).handler(({ request, response }) => {
if (!request.hasFiles?.()) return response.badRequest({ error: 'NO_FILES' })
return response.success({ files: request.files, fields: request.body })
})🔗 Related Packages
- @asterflow/plugin - built as a plugin with
Plugin.create() - @asterflow/router - adds
.multipart(...)toMethodandRouteMethodBuildervia declaration merging - @asterflow/request - extends
AsterRequestvia.extend()to attach parsed multipart data - @asterflow/response - returns
AsterResponseerrors for malformed or rejected multipart requests
📄 License
This project is licensed under the MIT License.
