Skip to content

File Uploads

Bun parses multipart/form-data natively, so uploaded files arrive as standard Web File objects on the request body. There’s no multer, and the FileInterceptor/@UploadedFile decorators from @nestjs/platform-express don’t apply here — use the patterns below instead.

The simplest path: the parsed multipart body exposes files and scalar fields together.

import { Body, Controller, Post } from '@nestjs/common';
@Controller('upload')
export class UploadController {
@Post('avatar')
async avatar(@Body() body: { file: File; userId: string }) {
const bytes = await body.file.arrayBuffer();
await Bun.write(`./uploads/${body.userId}`, bytes);
return { name: body.file.name, size: body.file.size, type: body.file.type };
}
}

For ergonomics close to platform-express, the package ships Bun-native param decorators that pull files straight out of the parsed body.

import { Controller, Post } from '@nestjs/common';
import { UploadedFile, UploadedFiles } from 'nestjs-platform-elysia';
@Controller('upload')
export class UploadController {
@Post('single')
single(@UploadedFile('file') file: File) {
return { name: file.name, size: file.size };
}
@Post('gallery')
gallery(@UploadedFiles('photos') photos: File[]) {
return { count: photos.length };
}
}
  • @UploadedFile(field) returns the File for that field (or undefined).
  • @UploadedFiles(field) returns an array — repeated form fields (photos, photos, …) are collected automatically.
  • Called with no argument, @UploadedFile() returns the first file in the body and @UploadedFiles() returns every file across all fields.

Pair uploads with Elysia’s t.File() for framework-level validation (size, MIME type) before your handler runs:

import { Post } from '@nestjs/common';
import { RouteSchema } from 'nestjs-platform-elysia';
import { t } from 'elysia';
@Post('document')
@RouteSchema({
body: t.Object({
file: t.File({ type: 'application/pdf', maxSize: '5m' }),
}),
})
upload(@UploadedFile('file') file: File) {
return { name: file.name };
}