Multipart Uploads
Handle file uploads using the @Multipart() decorator for processing multipart/form-data requests.
Enabling Multipart
Fastify Adapter
For Fastify, enable multipart support using @fastify/multipart:
const app = await NaviosFactory.create(AppModule, {
adapter: defineFastifyEnvironment(),
})
app.enableMultipart({
limits: {
fileSize: 10 * 1024 * 1024, // 10MB
files: 5, // Max 5 files
},
})
Bun Adapter
Bun natively supports multipart form data - no additional configuration needed. Just use @Multipart() endpoints directly.
Defining Upload Endpoints
Use declareMultipart from @navios/builder to define multipart endpoints:
import { builder } from '@navios/builder'
import { z } from 'zod'
const API = builder()
const uploadFile = API.declareMultipart({
url: '/files/upload',
responseSchema: z.object({
fileId: z.string(),
filename: z.string(),
size: z.number(),
}),
})
Handling Uploads
Use @Multipart() and MultipartParams:
import { Controller, Multipart, MultipartParams } from '@navios/core'
@Controller()
class FileController {
@Multipart(uploadFile)
async upload(params: MultipartParams<typeof uploadFile>) {
const file = params.file
// file.filename - Original filename
// file.mimetype - MIME type
// file.toBuffer() - Get file contents as Buffer
const buffer = await file.toBuffer()
const fileId = await this.storageService.save(buffer, file.filename)
return {
fileId,
filename: file.filename,
size: buffer.length,
}
}
}
Multiple Files
Handle multiple file uploads:
const uploadMultiple = API.declareMultipart({
url: '/files/batch',
responseSchema: z.array(
z.object({
fileId: z.string(),
filename: z.string(),
}),
),
})
@Controller()
class FileController {
@Multipart(uploadMultiple)
async uploadBatch(params: MultipartParams<typeof uploadMultiple>) {
const results = []
for await (const file of params.files) {
const buffer = await file.toBuffer()
const fileId = await this.storageService.save(buffer, file.filename)
results.push({ fileId, filename: file.filename })
}
return results
}
}
With Form Fields
Combine file uploads with form data:
const createPost = API.declareMultipart({
url: '/posts',
requestSchema: z.object({
title: z.string(),
content: z.string(),
}),
responseSchema: postSchema,
})
@Controller()
class PostController {
@Multipart(createPost)
async createPost(params: MultipartParams<typeof createPost>) {
const { title, content } = params.data
const image = params.file
let imageUrl = null
if (image) {
const buffer = await image.toBuffer()
imageUrl = await this.storageService.upload(buffer)
}
return this.postService.create({
title,
content,
imageUrl,
})
}
}
File Validation
Validate file properties:
@Controller()
class FileController {
@Multipart(uploadImage)
async uploadImage(params: MultipartParams<typeof uploadImage>) {
const file = params.file
// Validate MIME type
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']
if (!allowedTypes.includes(file.mimetype)) {
throw new BadRequestException(
'Invalid file type. Allowed: JPEG, PNG, WebP',
)
}
// Validate size
const buffer = await file.toBuffer()
if (buffer.length > 5 * 1024 * 1024) {
throw new BadRequestException('File too large. Max size: 5MB')
}
return this.imageService.process(buffer)
}
}
Streaming to Storage
For large files, stream directly to storage:
@Controller()
class FileController {
private s3 = inject(S3Service)
@Multipart(uploadLargeFile)
async uploadLarge(params: MultipartParams<typeof uploadLargeFile>) {
const file = params.file
// Stream directly to S3
const key = `uploads/${Date.now()}-${file.filename}`
await this.s3.upload({
Key: key,
Body: file.stream,
ContentType: file.mimetype,
})
return { key, filename: file.filename }
}
}
Configuration Options (Fastify)
These options apply to the Fastify adapter only:
app.enableMultipart({
limits: {
fieldNameSize: 100, // Max field name size
fieldSize: 1024 * 1024, // Max field value size (1MB)
fields: 10, // Max non-file fields
fileSize: 10 * 1024 * 1024, // Max file size (10MB)
files: 5, // Max number of files
headerPairs: 2000, // Max header key-value pairs
},
})
Error Handling
Handle upload errors:
@Multipart(uploadFile)
async upload(params: MultipartParams<typeof uploadFile>) {
try {
const file = params.file
if (!file) {
throw new BadRequestException('No file provided')
}
const buffer = await file.toBuffer()
return this.processFile(buffer)
} catch (error) {
if (error.code === 'LIMIT_FILE_SIZE') {
throw new BadRequestException('File too large')
}
if (error.code === 'LIMIT_FILE_COUNT') {
throw new BadRequestException('Too many files')
}
throw error
}
}