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.
Related Documentation
- Request Validation Documentation
- Handling Error Documentation
- Message Documentation
- Presign Documentation
Table of Contents
- Overview
- Related Documentation
- Decorators
- Enums
- Pipes
- CSV Import Flow
- Usage
- Error Handling
- Message Translation
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 configurationsfield: Field namemaxFiles: 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 filesjpg,jpeg,png
-
EnumFileExtensionDocument: Document filespdf,csv
-
EnumFileExtensionAudio: Audio filesmpeg,m4a,mp3
-
EnumFileExtensionVideo: Video filesmp4
-
EnumFileExtensionTemplate: Template fileshbs
-
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
EnumFileExtensionImagevalues - CSV enum is typically used with
FileCsvParsePipefor 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 fileUnsupportedMediaTypeException: 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:
- Receives parsed data from
FileCsvParsePipe - Transforms each row into the specified DTO class
- Validates using class-validator decorators
- Collects all validation errors with row context
- Throws
FileImportExceptionif 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 Type | Status Code | Message | Description |
|---|---|---|---|
| Invalid Extension | 5011 | file.error.extensionInvalid | File extension not in allowed list |
| Empty File | 422 | Unprocessable Entity | File buffer is empty or missing |
| Invalid Format | 415 | Unsupported Media Type | File format not supported (CSV) |
| Validation Failed | 5030 | file.error.validationDto | DTO 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:
- Validation errors are captured from class-validator
- Errors are passed to
MessageService.setValidationImportMessage() - Each constraint is translated using i18n keys:
request.error.{constraint} - 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.