File Uploads
⭐ Interview Importance: HIGH
⏱️ Revision Time: 6 min
File Uploads in a REST API require handling multipart/form-data instead of standard application/json. NestJS provides built-in interceptors leveraging the popular multer middleware to make this seamless.
Overview
When a user uploads a profile picture, the client cannot send it as a standard JSON object. They must construct a FormData object and send it as a multipart/form-data request.
To parse this binary data, NestJS uses multer under the hood. You apply a specific Interceptor to your route, which extracts the file from the request and makes it available to your controller method via the @UploadedFile() decorator.
Key Concepts
FileInterceptor('fieldName'): Extracts a single file from the request where the form field name matches'fieldName'.FilesInterceptor('fieldName'): Extracts an array of files uploaded under the same field name.FileFieldsInterceptor(): Extracts files uploaded under multiple different field names (e.g.,avatarandbackground).Express.Multer.File: The TypeScript type representing the extracted file object, containing the buffer, mime type, and original name.
Code Examples
1. Basic Single File Upload
This handles a single file uploaded with the field name file.
import { Controller, Post, UseInterceptors, UploadedFile, ParseFilePipe, MaxFileSizeValidator, FileTypeValidator } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
@Post('avatar')
// 1. Tell Nest to look for a 'file' field in the multipart data
@UseInterceptors(FileInterceptor('file'))
uploadAvatar(
// 2. Extract the file and validate it using built-in File Pipes
@UploadedFile(
new ParseFilePipe({
validators: [
// Ensure it's not massive (e.g., max 1MB)
new MaxFileSizeValidator({ maxSize: 1024 * 1024 }),
// Ensure it's actually an image
new FileTypeValidator({ fileType: '.(png|jpeg|jpg)' }),
],
}),
) file: Express.Multer.File,
) {
// 3. The file is now a buffer in memory.
// You would typically pass this buffer to an S3 upload service.
console.log(file.originalname);
console.log(file.mimetype);
console.log(file.size);
// Example: await this.s3Service.upload(file.buffer, file.originalname);
return { message: 'File uploaded successfully!' };
}
}
2. Multiple File Upload
Uploading an array of images for a product gallery.
import { Controller, Post, UseInterceptors, UploadedFiles } from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
@Controller('products')
export class ProductsController {
@Post('gallery')
// Expect up to 10 files under the 'images' field
@UseInterceptors(FilesInterceptor('images', 10))
uploadGallery(
// Notice it is @UploadedFiles() plural, and returns an Array!
@UploadedFiles() files: Array<Express.Multer.File>
) {
files.forEach(file => {
console.log(`Processing ${file.originalname}`);
});
return { message: `${files.length} files processed.` };
}
}
Best Practices
- Do not save to the local disk:
multercan be configured with adiskStorageengine to save the files directly to your server’s/uploadsfolder. Do not do this in modern architectures. If your app is deployed to a container (like Docker/Kubernetes) or a serverless environment, that local disk is ephemeral and will be wiped when the container restarts. Always usememoryStorage(the default) and stream thefile.bufferto a cloud storage provider like AWS S3 or Google Cloud Storage. - Always Validate: Never trust the user’s file. Always use
ParseFilePipewith aMaxFileSizeValidatorto prevent attackers from uploading 10GB files and crashing your server via OOM (Out of Memory) errors. Always validate the MIME type to prevent malicious script uploads.