Skip to Content
File Upload Documentation

File Upload Documentation

This documentation explains the features and usage of:

  • File Module: Located at src/common/file
  • Aws S3 Module: Located at src/modules/aws

Overview

The file upload module provides a comprehensive solution for handling file uploads in Complete NestJs Boilerplate. It includes decorators, pipes, services, and utilities for single/multiple file uploads, file validation, and CSV processing.

The module supports:

Direct Upload: Traditional multipart form-data upload where files are sent through the backend server. Ideal for small to medium files and when you need immediate server-side processing.

Table of Contents

Decorators

FileUploadSingle

Handles single file upload with configurable field name and size limits.

Parameters:

  • options.field (optional): Field name in form-data (default: 'file')
  • options.fileSize (optional): Maximum file size in bytes (default: FileSizeInBytes)

Example:

@FileUploadSingle({ field: 'photo', fileSize: bytes('5mb') })

FileUploadMultiple

Handles multiple files upload with the same field name.

Parameters:

  • options.field (optional): Field name in form-data (default: 'files')
  • options.maxFiles (optional): Maximum number of files (default: 2)
  • options.fileSize (optional): Maximum file size per file in bytes (default: FileSizeInBytes)

Example:

@FileUploadMultiple({ field: 'documents', maxFiles: 5 })

FileUploadMultipleFields

Handles multiple files from different form fields.

Parameters:

  • fields: Array of field configurations
    • field: Field name
    • maxFiles: Maximum files for this field
  • options.fileSize (optional): Maximum file size per file in bytes (default: FileSizeInBytes)

Example:

@FileUploadMultipleFields( [ { field: 'avatar', maxFiles: 1 }, { field: 'documents', maxFiles: 3 } ], { fileSize: bytes('15mb') } )

Enums

File extension enums for validation. These enums are used with FileExtensionPipe to restrict allowed file types for uploads.

Available Enums

  • EnumFileExtensionImage: Image files

    • jpg, jpeg, png
  • EnumFileExtensionDocument: Document files

    • pdf, csv
  • EnumFileExtensionAudio: Audio files

    • mpeg, m4a, mp3
  • EnumFileExtensionVideo: Video files

    • mp4
  • EnumFileExtensionTemplate: Template files

    • hbs
  • EnumFileExtension: Combined type of all file extensions

When to Use:

  • Combine multiple enums for flexible validation: [EnumFileExtensionImage.jpg, EnumFileExtensionDocument.pdf]
  • Use specific enum for strict type control: only EnumFileExtensionImage values
  • CSV enum is typically used with FileCsvParsePipe for data import features

Pipes

FileExtensionPipe

Validates uploaded file extensions against allowed types. This pipe checks the file extension and throws an error if the file type is not in the allowed list.

Usage: Pass an array of allowed file extensions from the enum constants. Works with both single file and multiple files uploads.

Throws:

  • UnsupportedMediaTypeException: When file extension is not in the allowed list

FileCsvParsePipe

Parses CSV (.csv) files into structured data array with rows and columns. This pipe converts raw file buffer into usable JavaScript objects using semicolon (;) as delimiter.

Returns: Array of parsed row objects T[]

Supports:

  • CSV files (.csv) with semicolon delimiter
  • Headers in first row become object property names
  • Empty lines are automatically skipped

Throws:

  • UnprocessableEntityException: Empty buffer or missing file
  • UnsupportedMediaTypeException: Invalid file extension

FileCsvValidationPipe

Transforms and validates CSV data using DTO classes with class-validator decorators. This pipe applies validation rules to each row of imported data and provides detailed error messages.

How it Works:

  1. Receives parsed data from FileCsvParsePipe
  2. Transforms each row into the specified DTO class
  3. Validates using class-validator decorators
  4. Collects all validation errors with row context
  5. Throws FileImportException if validation fails

Parameters:

  • DTO class for row validation

Throws:

  • FileImportException: Contains detailed validation errors with row context

CSV Import Flow

Understanding the flow of CSV file processing helps you implement robust data import features. The diagram below illustrates how uploaded CSV files are processed through validation and transformation pipelines.

Usage

Basic File Upload

Single and multiple file uploads with extension validation.

Single File Upload:

@Controller('users') export class UserController { @Post('/profile/upload/photo') @FileUploadSingle() @HttpCode(HttpStatus.OK) async uploadPhotoProfile( @UploadedFile( FileExtensionPipe([ EnumFileExtensionImage.jpeg, EnumFileExtensionImage.png, EnumFileExtensionImage.jpg ]) ) file: IFile ) { const filename = this.fileService.createRandomFilename({ path: 'profiles', prefix: 'photo', extension: this.fileService.extractExtensionFromFilename(file.originalname) }); await this.storageService.upload(file.buffer, filename); return { filename }; } }

Multiple Files Upload:

@Post('/documents/upload') @FileUploadMultiple({ maxFiles: 5 }) async uploadDocuments( @UploadedFiles( FileExtensionPipe([ EnumFileExtensionDocument.pdf, EnumFileExtensionDocument.csv ]) ) files: IFile[] ) { const uploadedFiles = []; for (const file of files) { const filename = this.fileService.createRandomFilename({ path: 'documents', prefix: 'doc', extension: this.fileService.extractExtensionFromFilename(file.originalname) }); await this.storageService.upload(file.buffer, filename); uploadedFiles.push(filename); } return { files: uploadedFiles }; }

CSV Import

Import and validate data from CSV files.

Basic Parsing:

interface UserImportDto { name: string; email: string; age: number; } @Post('/users/import/parse') @FileUploadSingle() async parseUsers( @UploadedFile( FileCsvParsePipe<UserImportDto> ) data: UserImportDto[] ) { // data contains parsed rows as plain objects return { totalRows: data.length, preview: data.slice(0, 5) // First 5 rows }; }

With Validation:

class UserImportDto { @IsString() @IsNotEmpty() name: string; @IsEmail() email: string; @IsInt() @Min(18) @Max(100) age: number; } @Post('/users/import') @FileUploadSingle() async importUsers( @UploadedFile( FileCsvParsePipe, new FileCsvValidationPipe(UserImportDto) ) data: UserImportDto[] ) { // Data is already validated, safe to use await this.userRepository.createMany(data); return { imported: data.length }; }

Multiple Field Upload

Upload files from different form fields simultaneously.

@Post('/profile/complete') @FileUploadMultipleFields([ { field: 'avatar', maxFiles: 1 }, { field: 'documents', maxFiles: 3 }, { field: 'certificates', maxFiles: 2 } ]) async uploadCompleteProfile( @UploadedFiles() files: { avatar?: IFile[], documents?: IFile[], certificates?: IFile[] } ) { const result = {}; if (files.avatar) { const avatar = files.avatar[0]; const filename = this.fileService.createRandomFilename({ path: 'avatars', prefix: 'avatar', extension: this.fileService.extractExtensionFromFilename(avatar.originalname) }); await this.storageService.upload(avatar.buffer, filename); result.avatar = filename; } if (files.documents) { result.documents = []; for (const doc of files.documents) { const filename = this.fileService.createRandomFilename({ path: 'documents', prefix: 'doc', extension: this.fileService.extractExtensionFromFilename(doc.originalname) }); await this.storageService.upload(doc.buffer, filename); result.documents.push(filename); } } return result; }

Error Handling

FileImportException

Thrown during CSV validation with detailed error context. This exception provides comprehensive information about validation failures including the exact row and validation errors.

Exception Structure:

{ statusCode: number; message: string; errors: Array<{ row: number; // Row index (0-based) errors: ValidationError[]; // class-validator errors }>; }

Common Errors

Error TypeStatus CodeMessageDescription
Invalid Extension5011file.error.extensionInvalidFile extension not in allowed list
Empty File422Unprocessable EntityFile buffer is empty or missing
Invalid Format415Unsupported Media TypeFile format not supported (CSV)
Validation Failed5030file.error.validationDtoDTO validation failed with details

Error Response Examples:

// Invalid Extension { "statusCode": 5011, "message": "file.error.extensionInvalid" } // Validation Errors { "statusCode": 5030, "message": "file.error.validationDto", "errors": [ { "row": 0, "errors": [ { "property": "email", "constraints": { "isEmail": "email must be an email" } }, { "property": "age", "constraints": { "min": "age must not be less than 18" } } ] } ] }

Message Translation

File validation errors are automatically translated using the i18n system. The FileCsvValidationPipe integrates with MessageService to provide localized error messages based on the user’s language preference.

How It Works:

  1. Validation errors are captured from class-validator
  2. Errors are passed to MessageService.setValidationImportMessage()
  3. Each constraint is translated using i18n keys: request.error.{constraint}
  4. Localized messages are returned in the error response

Custom Error Messages:

Add custom validation messages in your i18n language files for any class-validator constraint:

{ "error": { "min": "{property} must not be less than {value}", "max": "{property} must not be greater than {value}", "isEmail": "{property} must be a valid email address" } }

See Message Documentation for complete language configuration details.

Last updated on