Overview
NestJS provides a powerful module for handling form data with ease, taking advantage of TypeScript features for robustness and scalability in web application development.
Setting Up Your NestJS Project
Before handling form-data in NestJS, ensure you have Node.js installed. Create a new project using the Nest CLI:
nest new form-data-project
Once the installation is complete, navigate into your new project:
cd form-data-project
For handling form-data, we need to install additional packages. Run the following command to install them:
npm install @nestjs/platform-express multer
Basic Form Data Handling
To start handling simple form-data submissions, let’s create an endpoint that could receive a POST request with form-data.
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
@Post()
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
return 'File uploaded successfully!';
}
}
Advanced File Uploads
For more complex scenarios such as uploading multiple files, we can use the FilesInterceptor
.
import { Controller, Post, UseInterceptors, UploadedFiles } from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
@Controller('multi-upload')
export class MultiUploadController {
@Post()
@UseInterceptors(FilesInterceptor('files'))
uploadMultipleFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
console.log(files);
return 'Multiple files uploaded successfully!';
}
}
Custom Storage Engine
We can define a custom storage engine to control how files are stored. Let’s customize the storage using Multer.
import { diskStorage } from 'multer';
import { extname } from 'path';
export const customStorage = diskStorage({
destination: './uploads',
filename: (req, file, callback) => {
const randomName = Array(32)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('');
callback(null, `${randomName}${extname(file.originalname)}`);
},
});
Handling Form Fields
Beyond files, we often need to handle text fields. Here’s how you can access form fields along with the file upload:
import { Controller, Post, UseInterceptors, UploadedFile, Body } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Controller('field-upload')
export class FieldUploadController {
@Post()
@UseInterceptors(FileInterceptor('file'))
uploadFileAndFields(@UploadedFile() file: Express.Multer.File, @Body() body: any) {
console.log(file);
console.log(body);
return 'File and field data uploaded successfully!';
}
}
Validating Form Data
Validation ensures the integrity of the data. Use class-validator and class-transformer with DTOs (Data Transfer Objects) to validate form submissions:
import { IsNotEmpty, IsString } from 'class-validator';
export class CreateItemDto {
@IsNotEmpty()
@IsString()
readonly name: string;
}
In the controller:
import { Controller, Post, UsePipes, ValidationPipe, UseInterceptors, UploadedFile, Body } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { CreateItemDto } from './create-item.dto';
@Controller('validated-upload')
export class ValidatedUploadController {
@Post()
@UsePipes(new ValidationPipe({ whitelist: true }))
@UseInterceptors(FileInterceptor('file'))
uploadFileAndValidate(@UploadedFile() file: Express.Multer.File, @Body() createItemDto: CreateItemDto) {
console.log(file);
console.log(createItemDto);
return 'File and validated data uploaded successfully!';
}
}
Handling Async File Uploads
For asynchronous operations, such as interacting with a database when uploading files, you can return a promise:
import { Injectable } from '@nestjs/common';
@Injectable()
export class DatabaseService {
async saveFileData(file: Express.Multer.File): Promise<string> {
// Assume we save file to a database...
return 'Saved in DB successfully!';
}
}
Then in the controller:
import { Controller, Post, UseInterceptors, UploadedFile, Inject } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { DatabaseService } from './database.service';
@Controller('async-upload')
export class AsyncUploadController {
constructor(@Inject(DatabaseService) private databaseService: DatabaseService) {}
@Post()
@UseInterceptors(FileInterceptor('file'))
async uploadAsync(@UploadedFile() file: Express.Multer.File) {
return this.databaseService.saveFileData(file);
}
}
File Filtering and Size Limiting
To control the file type and size during uploads, NestJS offers file filtering and size limiting options:
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { multerOptions } from './multer-options';
@Controller('filter-upload')
export class FilterUploadController {
@Post()
@UseInterceptors(FileInterceptor('file', multerOptions))
uploadFilteredFile(@UploadedFile() file: Express.Multer.File) {
return 'Filtered file uploaded successfully!';
}
}
And in the multer options:
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
export const multerOptions: MulterOptions = {
fileFilter: (req, file, callback) => {
if (file.mimetype.match(/\/png|jpeg|jpg|gif$/)) {
callback(null, true);
} else {
callback(new Error('Unsupported file type'), false);
}
},
limits: { fileSize: 1024 * 1024 * 5 }, // Limit to 5MB
};
Conclusion
Throughout this tutorial, we’ve explored multiple facets of handling form-data in NestJS, showing how the framework’s modern TypeScript support can simplify this critical web development task. Whether it’s basic file uploads, validation, or custom configurations, NestJS provides the tools necessary for efficient and secure file handling.