Upload Guide
Why upload matters in Vona
Vona treats file upload as a request-path capability that should integrate cleanly with controller decorators, validation-style metadata, and automatic temporary-file handling.
That matters because upload flows often combine files, structured form fields, OpenAPI-facing request contracts, and cleanup responsibilities.
Core upload model
Vona implements upload capabilities on top of Busboy.
The most important framework-facing pieces are:
- the upload interceptor enabled by
@Core.fileUpload() - file and field parameter decorators under
@Arg.* - automatic temporary-file lifecycle handling
Upload interceptor
File upload is enabled through the upload interceptor.
Representative pattern:
@Web.post('file')
@Core.fileUpload()
@Api.contentType('application/json')
async uploadFile(@Arg.file('file1', v.title('Upload Single File')) file1: IUploadFile) {
return file1.file;
}The upload interceptor:
- parses incoming upload data
- stores uploaded files in temporary files
- exposes those files to controller parameters
- automatically cleans up the temporary files after the business logic completes
Single-file upload
Single-file upload usually uses @Arg.file(...).
@Arg.file('file1', v.title('Upload Single File')) file1: IUploadFileIUploadFile includes fields such as:
namefileinfo.filenameinfo.encodinginfo.mimeType
This gives controller code both the temporary file path and the original upload metadata.
Multiple-file upload
Multiple-file upload uses @Arg.files(...).
@Arg.files('files', v.title('Upload Multiple Files')) files: IUploadFile[]This is the preferred pattern when one field name represents an array of uploaded files.
Form fields alongside files
Upload flows often include normal form fields in addition to file data.
Vona supports both:
@Arg.field(...)@Arg.fields(...)
@Arg.field(...)
Use this when the frontend sends one field value directly, or a serialized array-like value under a single form entry.
Representative frontend shape:
const formData = new FormData();
formData.append('name', 'vona');
formData.append('tags', ['node', 'typescript']);@Arg.fields(...)
Use this when the frontend appends the same field key multiple times, for example multiple tags entries in FormData.
Representative frontend shape:
const formData = new FormData();
formData.append('name', 'vona');
formData.append('tags', 'node');
formData.append('tags', 'typescript');This distinction matters because frontend upload construction style affects how the backend should read field values.
JSON content type note
When an upload endpoint returns JSON data, it is useful to set:
@Api.contentType('application/json')That helps keep the API contract explicit even when the client upload request is multipart-based.
Relationship to controller design
Upload is best understood as a controller-facing request-path capability.
Read this guide together with:
These guides explain the broader request-decorator, validation, and contract-generation model that upload participates in.
A useful boundary is:
- the upload interceptor owns multipart parsing and temporary-file lifecycle
- controller decorators own request-shape declaration
- business logic should focus on what to do with the uploaded content once those two layers have normalized the input
Typical backend workflow
A practical upload flow often looks like this:
- enable the upload interceptor with
@Core.fileUpload() - declare file and form-field parameters with
@Arg.file,@Arg.files,@Arg.field, or@Arg.fields - apply titles or validation-oriented helpers where useful
- process the temporary files inside the controller or delegated service logic
- return the desired result while letting the interceptor handle cleanup
In the current repo, test upload controllers also demonstrate a broader shape with mixed usage such as:
- raw
@Arg.fields()access to all parsed fields - named
@Arg.field(...)extraction - named
@Arg.file(...)and@Arg.files(...)extraction in the same endpoint
That is a good reminder that upload endpoints can stay declarative even when multipart payloads become fairly rich.
Implementation checks for backend upload changes
When editing backend upload behavior, ask:
- is the request-path correctly using
@Core.fileUpload()? - should this parameter use
@Arg.file,@Arg.files,@Arg.field, or@Arg.fields? - does the frontend upload construction style match the backend field decorators?
- should the response contract explicitly declare JSON content type or related metadata?
That helps AI keep upload flows aligned with Vona’s real request and cleanup model.