CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nestjs--platform-express

Express.js HTTP platform adapter for NestJS applications with comprehensive file upload capabilities

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

file-upload.mddocs/

File Upload System

Comprehensive file upload functionality using Multer with interceptors for various upload scenarios, module configuration, validation, and error handling.

Capabilities

File Upload Interceptors

Interceptor functions that create NestJS interceptors for handling different file upload patterns.

Single File Upload

/**
 * Creates interceptor for single file upload handling
 * @param fieldName - Form field name for the file input
 * @param localOptions - Optional multer configuration for this interceptor
 * @returns NestJS interceptor class for single file uploads
 */
function FileInterceptor(
  fieldName: string,
  localOptions?: MulterOptions
): Type<NestInterceptor>;

Usage Examples:

import { FileInterceptor } from '@nestjs/platform-express';
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';

@Controller('upload')
export class UploadController {
  @Post('single')
  @UseInterceptors(FileInterceptor('file'))
  uploadSingle(@UploadedFile() file: Express.Multer.File) {
    return {
      filename: file.filename,
      size: file.size,
      mimetype: file.mimetype
    };
  }

  @Post('avatar')
  @UseInterceptors(FileInterceptor('avatar', {
    limits: { fileSize: 1024 * 1024 * 2 }, // 2MB limit
    fileFilter: (req, file, callback) => {
      if (file.mimetype.startsWith('image/')) {
        callback(null, true);
      } else {
        callback(new Error('Only image files allowed'), false);
      }
    }
  }))
  uploadAvatar(@UploadedFile() file: Express.Multer.File) {
    return { message: 'Avatar uploaded successfully' };
  }
}

Multiple Files Upload

/**
 * Creates interceptor for multiple files upload from single field
 * @param fieldName - Form field name for the file inputs
 * @param maxCount - Maximum number of files to accept
 * @param localOptions - Optional multer configuration for this interceptor
 * @returns NestJS interceptor class for multiple file uploads
 */
function FilesInterceptor(
  fieldName: string,
  maxCount?: number,
  localOptions?: MulterOptions
): Type<NestInterceptor>;

Usage Examples:

@Controller('upload')
export class UploadController {
  @Post('multiple')
  @UseInterceptors(FilesInterceptor('files', 5))
  uploadMultiple(@UploadedFiles() files: Express.Multer.File[]) {
    return {
      count: files.length,
      files: files.map(file => ({
        filename: file.filename,
        size: file.size
      }))
    };
  }

  @Post('gallery')
  @UseInterceptors(FilesInterceptor('images', 10, {
    limits: { fileSize: 1024 * 1024 * 5 }, // 5MB per file
    fileFilter: (req, file, callback) => {
      const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
      if (allowedTypes.includes(file.mimetype)) {
        callback(null, true);
      } else {
        callback(new Error('Invalid file type for gallery'), false);
      }
    }
  }))
  uploadGallery(@UploadedFiles() files: Express.Multer.File[]) {
    return { message: `${files.length} images uploaded to gallery` };
  }
}

File Fields Upload

/**
 * Creates interceptor for multiple files upload from multiple fields
 * @param uploadFields - Array of field configurations with names and max counts
 * @param localOptions - Optional multer configuration for this interceptor
 * @returns NestJS interceptor class for file fields uploads
 */
function FileFieldsInterceptor(
  uploadFields: MulterField[],
  localOptions?: MulterOptions
): Type<NestInterceptor>;

Usage Examples:

@Controller('upload')
export class UploadController {
  @Post('profile')
  @UseInterceptors(FileFieldsInterceptor([
    { name: 'avatar', maxCount: 1 },
    { name: 'background', maxCount: 1 },
    { name: 'documents', maxCount: 5 }
  ]))
  uploadProfile(@UploadedFiles() files: {
    avatar?: Express.Multer.File[],
    background?: Express.Multer.File[],
    documents?: Express.Multer.File[]
  }) {
    return {
      avatar: files.avatar?.[0]?.filename,
      background: files.background?.[0]?.filename,
      documents: files.documents?.length || 0
    };
  }

  @Post('mixed')
  @UseInterceptors(FileFieldsInterceptor([
    { name: 'logo', maxCount: 1 },
    { name: 'images', maxCount: 10 },
    { name: 'videos', maxCount: 3 }
  ], {
    limits: {
      fileSize: 1024 * 1024 * 50, // 50MB max per file
      files: 14 // Total file limit
    }
  }))
  uploadMixed(@UploadedFiles() files: Record<string, Express.Multer.File[]>) {
    return Object.keys(files).reduce((acc, key) => {
      acc[key] = files[key].length;
      return acc;
    }, {} as Record<string, number>);
  }
}

Any Files Upload

/**
 * Creates interceptor that accepts any files uploaded
 * @param localOptions - Optional multer configuration for this interceptor
 * @returns NestJS interceptor class for any file uploads
 */
function AnyFilesInterceptor(
  localOptions?: MulterOptions
): Type<NestInterceptor>;

No Files Upload

/**
 * Creates interceptor that accepts only text fields, no file uploads
 * @param localOptions - Optional multer configuration for this interceptor
 * @returns NestJS interceptor class that rejects file uploads
 */
function NoFilesInterceptor(
  localOptions?: MulterOptions
): Type<NestInterceptor>;

Usage Examples:

@Controller('upload')
export class UploadController {
  @Post('any')
  @UseInterceptors(AnyFilesInterceptor())
  uploadAny(@UploadedFiles() files: Express.Multer.File[]) {
    return { count: files.length };
  }

  @Post('text-only')
  @UseInterceptors(NoFilesInterceptor())
  textOnly(@Body() data: any) {
    // Only text fields will be parsed, file uploads will be rejected
    return { data };
  }
}

Multer Module Configuration

Dynamic module for configuring Multer globally across the application.

/**
 * Dynamic module for Multer file upload configuration
 * Provides global configuration for file upload behavior
 */
class MulterModule {
  /**
   * Register multer module with synchronous configuration
   * @param options - Multer configuration options
   * @returns Dynamic module for dependency injection
   */
  static register(options?: MulterModuleOptions): DynamicModule;

  /**
   * Register multer module with asynchronous configuration
   * @param options - Async configuration options with factory/class/existing patterns
   * @returns Dynamic module for dependency injection
   */
  static registerAsync(options: MulterModuleAsyncOptions): DynamicModule;
}

Usage Examples:

// Basic module registration
@Module({
  imports: [
    MulterModule.register({
      dest: './uploads',
      limits: {
        fileSize: 1024 * 1024 * 10 // 10MB
      }
    })
  ]
})
export class AppModule {}

// Async configuration with factory
@Module({
  imports: [
    MulterModule.registerAsync({
      useFactory: async (configService: ConfigService) => ({
        dest: configService.get('UPLOAD_PATH'),
        limits: {
          fileSize: configService.get('MAX_FILE_SIZE')
        }
      }),
      inject: [ConfigService]
    })
  ]
})
export class AppModule {}

// Async configuration with class
@Injectable()
export class MulterConfigService implements MulterOptionsFactory {
  createMulterOptions(): MulterModuleOptions {
    return {
      storage: multer.diskStorage({
        destination: './uploads',
        filename: (req, file, cb) => {
          const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
          cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
        }
      })
    };
  }
}

@Module({
  imports: [
    MulterModule.registerAsync({
      useClass: MulterConfigService
    })
  ]
})
export class AppModule {}

File Upload Interfaces

Type definitions for Multer configuration and file upload handling.

/**
 * Configuration options for Multer file uploads
 * Controls storage, limits, filtering, and processing behavior
 */
interface MulterOptions {
  /** Destination directory for uploaded files */
  dest?: string;
  
  /** Custom storage engine configuration */
  storage?: any;
  
  /** File size and count limits */
  limits?: {
    /** Max field name size in bytes */
    fieldNameSize?: number;
    /** Max field value size in bytes */
    fieldSize?: number;
    /** Max number of non-file fields */
    fields?: number;
    /** Max file size in bytes */
    fileSize?: number;
    /** Max number of file fields */
    files?: number;
    /** Max number of parts (fields + files) */
    parts?: number;
    /** Max number of header key-value pairs */
    headerPairs?: number;
  };
  
  /** Preserve file path information */
  preservePath?: boolean;
  
  /** File filtering function */
  fileFilter?: (
    req: any,
    file: any,
    callback: (error: Error | null, acceptFile: boolean) => void
  ) => void;
}

/**
 * Field configuration for multi-field file uploads
 * Defines field name and maximum file count per field
 */
interface MulterField {
  /** Form field name */
  name: string;
  /** Maximum number of files for this field */
  maxCount?: number;
}

/**
 * Type alias for module-level Multer options
 */
type MulterModuleOptions = MulterOptions;

/**
 * Factory interface for creating Multer options
 * Used with async configuration patterns
 */
interface MulterOptionsFactory {
  createMulterOptions(): Promise<MulterModuleOptions> | MulterModuleOptions;
}

/**
 * Async configuration options for MulterModule
 * Supports factory, class, and existing provider patterns
 */
interface MulterModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
  /** Use existing provider */
  useExisting?: Type<MulterOptionsFactory>;
  /** Use class as provider */
  useClass?: Type<MulterOptionsFactory>;
  /** Use factory function */
  useFactory?: (...args: any[]) => Promise<MulterModuleOptions> | MulterModuleOptions;
  /** Dependencies to inject into factory */
  inject?: any[];
}

File Upload Constants

Constants used for dependency injection and error handling.

/** Injection token for Multer module options */
const MULTER_MODULE_OPTIONS: string = 'MULTER_MODULE_OPTIONS';

Error Handling

Utility functions and constants for handling file upload errors.

/**
 * Transform Multer and Busboy errors into appropriate NestJS HTTP exceptions
 * @param error - Error object from Multer or Busboy with optional field information
 * @returns Transformed error appropriate for HTTP responses
 */
function transformException(
  error: (Error & { field?: string }) | undefined
): any;

/** Standard Multer error messages for various upload limits and validation failures */
const multerExceptions: {
  LIMIT_PART_COUNT: string;
  LIMIT_FILE_SIZE: string;
  LIMIT_FILE_COUNT: string;
  LIMIT_FIELD_KEY: string;
  LIMIT_FIELD_VALUE: string;
  LIMIT_FIELD_COUNT: string;
  LIMIT_UNEXPECTED_FILE: string;
};

/** Standard Busboy error messages for multipart parsing failures */
const busboyExceptions: {
  LIMIT_PART_COUNT: string;
  LIMIT_FILE_SIZE: string;
  LIMIT_FILE_COUNT: string;
  LIMIT_FIELD_KEY: string;
  LIMIT_FIELD_VALUE: string;
  LIMIT_FIELD_COUNT: string;
  LIMIT_UNEXPECTED_FILE: string;
  MISSING_FIELD_NAME: string;
};

Usage Examples:

// Custom error handling in file filter
const fileFilter = (req: any, file: any, callback: any) => {
  if (!file.mimetype.startsWith('image/')) {
    // This will be caught and transformed by transformException
    return callback(new Error('Only image files are allowed'), false);
  }
  callback(null, true);
};

// Global exception filter for file upload errors
@Catch()
export class FileUploadExceptionFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    
    if (exception.code === 'LIMIT_FILE_SIZE') {
      return response.status(413).json({
        statusCode: 413,
        message: 'File too large',
        error: 'Payload Too Large'
      });
    }
    
    // Handle other multer errors...
  }
}

Advanced Configuration Examples

Real-world configuration patterns for complex file upload scenarios.

// Custom storage configuration
import * as multer from 'multer';
import * as path from 'path';

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const uploadPath = path.join('./uploads', req.user.id);
    cb(null, uploadPath);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const filename = file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname);
    cb(null, filename);
  }
});

// Comprehensive file validation
const fileFilter = (req: any, file: any, callback: any) => {
  const allowedTypes = {
    'image/jpeg': ['.jpg', '.jpeg'],
    'image/png': ['.png'],
    'image/gif': ['.gif'],
    'application/pdf': ['.pdf'],
    'text/plain': ['.txt']
  };
  
  if (allowedTypes[file.mimetype]) {
    const ext = path.extname(file.originalname).toLowerCase();
    if (allowedTypes[file.mimetype].includes(ext)) {
      callback(null, true);
    } else {
      callback(new Error('File extension does not match MIME type'), false);
    }
  } else {
    callback(new Error('Unsupported file type'), false);
  }
};

// Production-ready configuration
@Module({
  imports: [
    MulterModule.registerAsync({
      useFactory: (configService: ConfigService) => ({
        storage: storage,
        fileFilter: fileFilter,
        limits: {
          fileSize: configService.get('MAX_FILE_SIZE', 1024 * 1024 * 10), // 10MB
          files: configService.get('MAX_FILES', 5),
          fields: configService.get('MAX_FIELDS', 10)
        }
      }),
      inject: [ConfigService]
    })
  ]
})
export class AppModule {}

docs

application-interface.md

express-adapter.md

file-upload.md

index.md

tile.json