Skip to main content

Validation & limits

Declare limits as data — at the module level (applies to every route) and/or per route (merged over the module default). Rejections throw typed 400s before your handler runs.

// module-wide default
NestFileStorageModule.forRoot({
default: 'local',
drivers: { /* ... */ },
validation: { maxSize: 10 * 1024 * 1024 }, // 10 MB everywhere
});
// per route (merged over the module default)
FileStorageInterceptor('image', {
validation: {
maxSize: 5 * 1024 * 1024, // bytes
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/*'], // wildcards supported
allowedExtensions: ['.jpg', '.png'], // case-insensitive
maxFiles: 10, // total files per request
fileFilter: (req, file, cb) => cb(null, true), // escape hatch (runs last)
},
});

What each rule does

RuleBecomesThrows on violation
maxSizeMulter limits.fileSizeFileTooLargeException
maxFilesMulter limits.filesTooManyFilesException
allowedMimeTypes / allowedExtensionsa generated fileFilterInvalidFileTypeException

All three exceptions extend BadRequestException, so they surface as standard 400 responses with a clear message:

{ "message": "File type \"text/plain\" is not allowed. Allowed: image/jpeg, image/png.", "error": "Bad Request", "statusCode": 400 }
{ "message": "File exceeds the maximum allowed size of 5 MB.", "error": "Bad Request", "statusCode": 400 }

MIME wildcards & extensions

  • allowedMimeTypes matches exact (image/png) or wildcard (image/*, */*).
  • allowedExtensions is case-insensitive and accepts entries with or without a leading dot (png or .png).

Cross-field rules

For rules that depend on multiple files/fields together, use afterUpload — e.g. "at least 2 images", "a cover is required when gallery is present".

:::tip Don't validate inside fileName In v1 it was common to throw from fileName(). Use declarative validation instead — it runs at the right time (size is enforced during streaming) and returns typed errors. :::