How to handle form-data in NestJS

Updated: December 31, 2023 By: Guest Contributor Post a comment

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.