Lesson 46 of 5020 min read

NestJS File Upload Tutorial Using Multer

Learn how to handle file uploads in NestJS using Multer, including single and multiple file uploads, storage configuration, and file validation.

Author: CodersNexus

NestJS File Upload Tutorial Using Multer

User-uploaded images, documents, and other files are a common requirement in real applications, profile pictures, resume uploads, product images, and NestJS handles this through its integration with Multer, the standard Express middleware for handling multipart/form-data file uploads.

NestJS File Upload Multer: Learning Objectives

  • Understand how file uploads work using the multipart/form-data content type.
  • Handle a single file upload using NestJS's FileInterceptor and @UploadedFile().
  • Handle multiple file uploads using FilesInterceptor.
  • Configure Multer's storage engine to control where and how uploaded files are saved.
  • Validate uploaded files by size and type using NestJS's ParseFilePipe.

NestJS File Upload Multer: Key Terms and Definitions

  • multipart/form-data: An HTTP content type specifically designed for submitting forms that include file data alongside regular fields.
  • Multer: A Node.js middleware for handling multipart/form-data, used by NestJS to process file uploads.
  • FileInterceptor: A NestJS interceptor that processes a single uploaded file from a specified form field, making it available via @UploadedFile().
  • FilesInterceptor: A NestJS interceptor that processes multiple uploaded files from a specified form field, made available via @UploadedFiles().
  • ParseFilePipe: A NestJS pipe used to validate uploaded files, commonly checking file size and MIME type before they reach the route handler.

How NestJS File Upload Multer Works: Detailed Explanation

Regular JSON request bodies can't carry binary file data directly, which is why file uploads use a different HTTP content type, multipart/form-data, that can bundle both regular form fields and raw file data together in a single request. Multer is the standard Express (and therefore NestJS-compatible) middleware that parses this multipart content, extracting uploaded files and making them available to your application.

NestJS provides its own thin, convenient wrapper around Multer through interceptors. For a single file upload, you apply @UseInterceptors(FileInterceptor('fieldName')) to a route handler, where 'fieldName' matches the name of the form field the client uses to submit the file. The uploaded file then becomes available inside the handler using the @UploadedFile() parameter decorator, giving you access to properties like the file's original name, size, MIME type, and either its buffer (if kept in memory) or its saved path (if written to disk), depending on your storage configuration.

For multiple files, either from a single field accepting several files or from several distinctly named fields, FilesInterceptor (plural) and the corresponding @UploadedFiles() decorator work almost identically, simply handling an array of files instead of one.

By default, Multer keeps uploaded files in memory as a buffer, which is fine for small files or when you intend to immediately process and forward the file elsewhere (like uploading it to cloud storage), but for larger files or when you want to persist them directly to the local filesystem, you configure Multer's diskStorage option, specifying a destination folder and a filename function to control exactly how and where uploaded files are saved.

Finally, accepting arbitrary file uploads without validation is a real security and stability risk, a malicious or careless client could upload an enormous file, or a file type your application isn't prepared to handle. NestJS's ParseFilePipe, applied to the @UploadedFile() parameter, lets you specify validators, most commonly a maximum file size validator and a file type (MIME type) validator, automatically rejecting uploads that don't meet these requirements with a clear error before the file ever reaches your business logic.

Interview-Friendly Explanation

A strong interview or viva answer for nestjs file upload multer must go beyond a one-line definition. Interviewers evaluating experienced backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters in production Node.js applications. Use the four-point framework below when answering under time pressure.

  • Definition point: multipart/form-data: An HTTP content type specifically designed for submitting forms that include file data alongside regular fields.
  • Working point: Handle a single file upload using NestJS's FileInterceptor and @UploadedFile().
  • Example point: Profile picture and avatar uploads across virtually every application with user accounts follow this exact FileInterceptor plus ParseFilePipe pattern, validating both size and image type before accepting an upload.
  • Conclusion point: File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.

How to Answer This in a Technical Interview

  1. Give a two-to-three sentence definition using correct NestJS and Node.js terminology.
  2. Add one specific example drawn from a real backend scenario such as an e-commerce, fintech, or SaaS API.
  3. Mention a relevant trade-off or alternative approach, since interviewers often probe this contrast.
  4. Close with one benefit, limitation, or production use case.
  5. Avoid vague answers that only restate the term — interviewers filter these out immediately.

Practical Scenario

Imagine you are scaling a production backend for a SaaS product, similar to systems used at companies like Razorpay, Freshworks, or Zomato. Every lesson in this module maps directly to a decision you will make while making that backend more advanced, performant, and maintainable at scale.

NestJS File Upload Multer: Architecture and Flow Diagram

Visualize the file upload flow:

[Client submits multipart/form-data with a file field] --> [FileInterceptor('file') intercepts and Multer parses the upload] --> [ParseFilePipe validates size/type] --> [Valid? @UploadedFile() populates the file parameter, route handler executes | Invalid? Rejected with a clear error before the handler runs]

NestJS File Upload Multer: Component Reference Table

ComponentPurpose
FileInterceptor('field')Processes a single uploaded file from the named form field
FilesInterceptor('field')Processes multiple uploaded files from the named form field
@UploadedFile()Injects the processed single file into the route handler
@UploadedFiles()Injects the processed array of files into the route handler
ParseFilePipeValidates uploaded files by size, type, or other custom rules

NestJS File Upload Multer: NestJS Code Example

// npm install @nestjs/platform-express multer
// npm install --save-dev @types/multer

import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile,
  ParseFilePipe,
  MaxFileSizeValidator,
  FileTypeValidator,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';

@Controller('uploads')
export class UploadsController {
  @Post('avatar')
  @UseInterceptors(
    FileInterceptor('avatar', {
      storage: diskStorage({
        destination: './uploads/avatars',
        filename: (req, file, callback) => {
          const uniqueName = `${Date.now()}-${file.originalname}`;
          callback(null, uniqueName);
        },
      }),
    }),
  )
  uploadAvatar(
    @UploadedFile(
      new ParseFilePipe({
        validators: [
          new MaxFileSizeValidator({ maxSize: 2 * 1024 * 1024 }), // 2MB limit
          new FileTypeValidator({ fileType: '.(png|jpeg|jpg)' }),
        ],
      }),
    )
    file: Express.Multer.File,
  ) {
    return {
      message: 'Avatar uploaded successfully',
      filename: file.filename,
      size: file.size,
    };
  }
}

FileInterceptor('avatar') tells NestJS to expect a single file submitted under the 'avatar' form field, configuring Multer's diskStorage to save uploaded files into a specific destination folder with a uniquely generated filename (prefixing the original name with the current timestamp to avoid collisions). The @UploadedFile() decorator is combined with a ParseFilePipe specifying two validators: MaxFileSizeValidator rejecting any file larger than 2MB, and FileTypeValidator only allowing PNG or JPEG files, both automatically enforced before the route handler's body ever executes. If validation passes, the handler receives the fully processed file object, including its saved filename and size, ready to use, for example, to update a user's profile record with the new avatar's path.

Real-World NestJS File Upload Multer Industry Examples

  • Profile picture and avatar uploads across virtually every application with user accounts follow this exact FileInterceptor plus ParseFilePipe pattern, validating both size and image type before accepting an upload.
  • Document management systems and job application portals commonly use FilesInterceptor to accept multiple supporting documents (resume, cover letter, portfolio) in a single form submission.
  • E-commerce platforms handling vendor-uploaded product images enforce strict file type and size validation exactly like this lesson's example, protecting against oversized or malicious file uploads.
  • Applications integrating with cloud storage (like AWS S3) often skip Multer's diskStorage entirely, instead keeping the file in memory as a buffer and immediately forwarding it to the cloud storage SDK rather than saving it to the local filesystem.

NestJS File Upload Multer Interview Questions and Answers

Q1. What HTTP content type is used for file uploads, and why can't a regular JSON body be used instead?

Short answer: File uploads use the multipart/form-data content type, which can bundle both regular form fields and raw binary file data together in a single request. A regular JSON request body cannot natively carry raw binary file data, which is why this distinct content type and corresponding parsing middleware, Multer, are needed.

Detailed explanation: Regular JSON request bodies can't carry binary file data directly, which is why file uploads use a different HTTP content type, multipart/form-data, that can bundle both regular form fields and raw file data together in a single request. Multer is the standard Express (and therefore NestJS-compatible) middleware that parses this multipart content, extracting uploaded files and making them available to your application. NestJS provides its own thin, convenient wrapper around Multer through interceptors. For a single file upload, you apply @UseInterceptors(FileInterceptor('fieldName')) to a route handler, where 'fieldName' matches the name of the form field the client uses to submit the file. The uploaded file then becomes available inside the handler using the @UploadedFile() parameter decorator, giving you access to properties like the file's original name, size, MIME type, and either its buffer (if kept in memory) or its saved path (if written to disk), depending on your storage configuration. For multiple files, either from a single field accepting several files or from several distinctly named fields, FilesInterceptor (plural) and the corresponding @UploadedFiles() decorator work almost identically, simply handling an array of files instead of one. By default, Multer keeps uploaded files in memory as a buffer, which is fine for small files or when you intend to immediately process and forward the file elsewhere (like uploading it to cloud storage), but for larger files or when you want to persist them directly to the local filesystem, you configure Multer's diskStorage option, specifying a destination folder and a filename function to control exactly how and where uploaded files are saved. Finally, accepting arbitrary file uploads without validation is a real security and stability risk, a malicious or careless client could upload an enormous file, or a file type your application isn't prepared to handle. NestJS's ParseFilePipe, applied to the @UploadedFile() parameter, lets you specify validators, most commonly a maximum file size validator and a file type (MIME type) validator, automatically rejecting uploads that don't meet these requirements with a clear error before the file ever reaches your business logic.

Practical example: Profile picture and avatar uploads across virtually every application with user accounts follow this exact FileInterceptor plus ParseFilePipe pattern, validating both size and image type before accepting an upload.

Interview tip: File uploads use multipart/form-data, parsed by Multer via NestJS's platform-express integration.

Revision hook: File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.

Q2. How would you handle a single file upload in a NestJS route?

Short answer: You apply @UseInterceptors(FileInterceptor('fieldName')) to the route handler, where 'fieldName' matches the client's form field name, and access the processed file inside the handler using the @UploadedFile() parameter decorator, which provides properties like the file's size, MIME type, and saved path or buffer.

Detailed explanation: FileInterceptor('avatar') tells NestJS to expect a single file submitted under the 'avatar' form field, configuring Multer's diskStorage to save uploaded files into a specific destination folder with a uniquely generated filename (prefixing the original name with the current timestamp to avoid collisions). The @UploadedFile() decorator is combined with a ParseFilePipe specifying two validators: MaxFileSizeValidator rejecting any file larger than 2MB, and FileTypeValidator only allowing PNG or JPEG files, both automatically enforced before the route handler's body ever executes. If validation passes, the handler receives the fully processed file object, including its saved filename and size, ready to use, for example, to update a user's profile record with the new avatar's path.

Practical example: Document management systems and job application portals commonly use FilesInterceptor to accept multiple supporting documents (resume, cover letter, portfolio) in a single form submission.

Interview tip: FileInterceptor + @UploadedFile() for single files; FilesInterceptor + @UploadedFiles() for multiple files.

Revision hook: NestJS's FileInterceptor/FilesInterceptor pair provides a clean, declarative wrapper around Multer's underlying parsing logic.

Q3. How would you validate an uploaded file's size and type in NestJS?

Short answer: You wrap the @UploadedFile() decorator with a ParseFilePipe, configured with validators such as MaxFileSizeValidator (to enforce a maximum file size) and FileTypeValidator (to restrict allowed MIME types), which automatically reject non-conforming uploads with a clear error before the route handler executes.

Detailed explanation: File uploads in NestJS are handled through Multer, the standard middleware for parsing multipart/form-data requests, wrapped in NestJS's own FileInterceptor (for a single file, paired with @UploadedFile()) and FilesInterceptor (for multiple files, paired with @UploadedFiles()). Multer's storage engine can be configured with diskStorage to control exactly where uploaded files are saved and how their filenames are generated, commonly incorporating a timestamp or unique identifier to avoid collisions. Critically, uploaded files should always be validated using ParseFilePipe combined with validators like MaxFileSizeValidator and FileTypeValidator, rejecting oversized or unexpected file types automatically before they ever reach business logic, protecting the application from both security risks and unexpected failures.

Practical example: E-commerce platforms handling vendor-uploaded product images enforce strict file type and size validation exactly like this lesson's example, protecting against oversized or malicious file uploads.

Interview tip: diskStorage configures where and how uploaded files are saved, including generating unique filenames.

Revision hook: Validating file size and type isn't optional for any production application accepting user uploads.

Q4. What is the difference between FileInterceptor and FilesInterceptor?

Short answer: FileInterceptor (singular) handles a single uploaded file from one named form field, paired with @UploadedFile(). FilesInterceptor (plural) handles multiple uploaded files, either from one field accepting several files or multiple fields, paired with the @UploadedFiles() decorator, which provides an array of processed files instead of just one.

Detailed explanation: Regular JSON request bodies can't carry binary file data directly, which is why file uploads use a different HTTP content type, multipart/form-data, that can bundle both regular form fields and raw file data together in a single request. Multer is the standard Express (and therefore NestJS-compatible) middleware that parses this multipart content, extracting uploaded files and making them available to your application. NestJS provides its own thin, convenient wrapper around Multer through interceptors. For a single file upload, you apply @UseInterceptors(FileInterceptor('fieldName')) to a route handler, where 'fieldName' matches the name of the form field the client uses to submit the file. The uploaded file then becomes available inside the handler using the @UploadedFile() parameter decorator, giving you access to properties like the file's original name, size, MIME type, and either its buffer (if kept in memory) or its saved path (if written to disk), depending on your storage configuration. For multiple files, either from a single field accepting several files or from several distinctly named fields, FilesInterceptor (plural) and the corresponding @UploadedFiles() decorator work almost identically, simply handling an array of files instead of one. By default, Multer keeps uploaded files in memory as a buffer, which is fine for small files or when you intend to immediately process and forward the file elsewhere (like uploading it to cloud storage), but for larger files or when you want to persist them directly to the local filesystem, you configure Multer's diskStorage option, specifying a destination folder and a filename function to control exactly how and where uploaded files are saved. Finally, accepting arbitrary file uploads without validation is a real security and stability risk, a malicious or careless client could upload an enormous file, or a file type your application isn't prepared to handle. NestJS's ParseFilePipe, applied to the @UploadedFile() parameter, lets you specify validators, most commonly a maximum file size validator and a file type (MIME type) validator, automatically rejecting uploads that don't meet these requirements with a clear error before the file ever reaches your business logic.

Practical example: Applications integrating with cloud storage (like AWS S3) often skip Multer's diskStorage entirely, instead keeping the file in memory as a buffer and immediately forwarding it to the cloud storage SDK rather than saving it to the local filesystem.

Interview tip: ParseFilePipe with MaxFileSizeValidator and FileTypeValidator enforces size and type constraints before the handler runs.

Revision hook: Where uploaded files are ultimately stored (local disk vs. cloud storage) is an important architectural decision beyond just NestJS's handling of the upload itself.

NestJS File Upload Multer MCQs and Practice Questions

1. Which HTTP content type is used for submitting file uploads?

  1. application/json
  2. multipart/form-data
  3. text/plain
  4. application/xml

Answer: B. multipart/form-data

Explanation: multipart/form-data is the standard content type for HTTP requests that need to include both regular form fields and raw binary file data together.

Concept link: File uploads use multipart/form-data, parsed by Multer via NestJS's platform-express integration.

Why this matters: File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.

2. Which NestJS interceptor handles a single uploaded file from a named form field?

  1. FilesInterceptor
  2. FileInterceptor
  3. CacheInterceptor
  4. LoggingInterceptor

Answer: B. FileInterceptor

Explanation: FileInterceptor (singular) processes a single uploaded file from the specified form field name, working alongside the @UploadedFile() decorator.

Concept link: FileInterceptor + @UploadedFile() for single files; FilesInterceptor + @UploadedFiles() for multiple files.

Why this matters: NestJS's FileInterceptor/FilesInterceptor pair provides a clean, declarative wrapper around Multer's underlying parsing logic.

3. Which decorator is used to access an uploaded file inside a NestJS route handler?

  1. @Body()
  2. @Param()
  3. @UploadedFile()
  4. @Query()

Answer: C. @UploadedFile()

Explanation: @UploadedFile() specifically retrieves the file processed by FileInterceptor, giving the route handler access to properties like the file's size, type, and saved location.

Concept link: diskStorage configures where and how uploaded files are saved, including generating unique filenames.

Why this matters: Validating file size and type isn't optional for any production application accepting user uploads.

4. Which NestJS pipe is used to validate an uploaded file's size and MIME type?

  1. ValidationPipe
  2. ParseIntPipe
  3. ParseFilePipe
  4. DefaultValuePipe

Answer: C. ParseFilePipe

Explanation: ParseFilePipe, combined with validators like MaxFileSizeValidator and FileTypeValidator, checks an uploaded file against size and type rules before it reaches the route handler's logic.

Concept link: ParseFilePipe with MaxFileSizeValidator and FileTypeValidator enforces size and type constraints before the handler runs.

Why this matters: Where uploaded files are ultimately stored (local disk vs. cloud storage) is an important architectural decision beyond just NestJS's handling of the upload itself.

Common NestJS File Upload Multer Mistakes to Avoid

  • Not validating uploaded files at all, allowing arbitrarily large or unexpected file types to be accepted, creating a security and stability risk.
  • Forgetting to configure a unique filename strategy in diskStorage, causing uploaded files with the same original name to overwrite each other.
  • Confusing FileInterceptor/@UploadedFile() (singular) with FilesInterceptor/@UploadedFiles() (plural) and their expected data shapes.
  • Saving uploaded files directly to the local filesystem in environments (like many cloud deployments) where the filesystem isn't persistent, losing uploaded files on redeployment or restart.

NestJS File Upload Multer: Interview Notes and Exam Tips

  • File uploads use multipart/form-data, parsed by Multer via NestJS's platform-express integration.
  • FileInterceptor + @UploadedFile() for single files; FilesInterceptor + @UploadedFiles() for multiple files.
  • diskStorage configures where and how uploaded files are saved, including generating unique filenames.
  • ParseFilePipe with MaxFileSizeValidator and FileTypeValidator enforces size and type constraints before the handler runs.

Key NestJS File Upload Multer Takeaways

  • File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.
  • NestJS's FileInterceptor/FilesInterceptor pair provides a clean, declarative wrapper around Multer's underlying parsing logic.
  • Validating file size and type isn't optional for any production application accepting user uploads.
  • Where uploaded files are ultimately stored (local disk vs. cloud storage) is an important architectural decision beyond just NestJS's handling of the upload itself.

NestJS File Upload Multer: Summary

File uploads in NestJS are handled through Multer, the standard middleware for parsing multipart/form-data requests, wrapped in NestJS's own FileInterceptor (for a single file, paired with @UploadedFile()) and FilesInterceptor (for multiple files, paired with @UploadedFiles()). Multer's storage engine can be configured with diskStorage to control exactly where uploaded files are saved and how their filenames are generated, commonly incorporating a timestamp or unique identifier to avoid collisions. Critically, uploaded files should always be validated using ParseFilePipe combined with validators like MaxFileSizeValidator and FileTypeValidator, rejecting oversized or unexpected file types automatically before they ever reach business logic, protecting the application from both security risks and unexpected failures.

Frequently Asked Questions

You need to install both @nestjs/platform-express (which provides the FileInterceptor/FilesInterceptor wrappers) and multer itself as separate npm packages, along with @types/multer as a development dependency for TypeScript type definitions. In interviews, tie this back to: File uploads use multipart/form-data, parsed by Multer via NestJS's platform-express integration. In real applications, consider this example: Profile picture and avatar uploads across virtually every application with user accounts follow this exact FileInterceptor plus ParseFilePipe pattern, validating both size and image type before accepting an upload. Key revision takeaway: File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.

Not necessarily; many production applications, especially those deployed to cloud platforms without persistent local storage, instead keep uploaded files in memory as a buffer (Multer's default without diskStorage configured) and immediately forward them to a cloud storage service like AWS S3, rather than writing to the local disk at all. In interviews, tie this back to: FileInterceptor + @UploadedFile() for single files; FilesInterceptor + @UploadedFiles() for multiple files. In real applications, consider this example: Document management systems and job application portals commonly use FilesInterceptor to accept multiple supporting documents (resume, cover letter, portfolio) in a single form submission. Key revision takeaway: NestJS's FileInterceptor/FilesInterceptor pair provides a clean, declarative wrapper around Multer's underlying parsing logic.

ParseFilePipe automatically rejects the request with a validation error before it reaches the route handler's logic, meaning your business logic never needs to manually check the file size itself, since oversized files are filtered out at the pipe level. In interviews, tie this back to: diskStorage configures where and how uploaded files are saved, including generating unique filenames. In real applications, consider this example: E-commerce platforms handling vendor-uploaded product images enforce strict file type and size validation exactly like this lesson's example, protecting against oversized or malicious file uploads. Key revision takeaway: Validating file size and type isn't optional for any production application accepting user uploads.

Yes, this is typically handled using FileFieldsInterceptor (a variant supporting multiple distinctly named fields, each potentially with its own file count limit), combined with the @UploadedFiles() decorator, which then returns an object keyed by each field name containing its respective uploaded file(s). In interviews, tie this back to: ParseFilePipe with MaxFileSizeValidator and FileTypeValidator enforces size and type constraints before the handler runs. In real applications, consider this example: Applications integrating with cloud storage (like AWS S3) often skip Multer's diskStorage entirely, instead keeping the file in memory as a buffer and immediately forwarding it to the cloud storage SDK rather than saving it to the local filesystem. Key revision takeaway: Where uploaded files are ultimately stored (local disk vs. cloud storage) is an important architectural decision beyond just NestJS's handling of the upload itself.

Using ParseFilePipe's FileTypeValidator, specifying a regular expression matching the allowed MIME types or file extensions (such as image formats like PNG, JPEG, and GIF), automatically rejecting any upload that doesn't match this pattern. In interviews, tie this back to: File uploads use multipart/form-data, parsed by Multer via NestJS's platform-express integration. In real applications, consider this example: Profile picture and avatar uploads across virtually every application with user accounts follow this exact FileInterceptor plus ParseFilePipe pattern, validating both size and image type before accepting an upload. Key revision takeaway: File uploads require a fundamentally different content type and handling approach compared to regular JSON APIs.

Yes, it's a strongly recommended security practice; relying on a client-provided original filename directly (without sanitization or renaming, as shown in this lesson's timestamp-prefixing example) can expose your application to path traversal risks or filename collisions, making a controlled, generated filename strategy important. In interviews, tie this back to: FileInterceptor + @UploadedFile() for single files; FilesInterceptor + @UploadedFiles() for multiple files. In real applications, consider this example: Document management systems and job application portals commonly use FilesInterceptor to accept multiple supporting documents (resume, cover letter, portfolio) in a single form submission. Key revision takeaway: NestJS's FileInterceptor/FilesInterceptor pair provides a clean, declarative wrapper around Multer's underlying parsing logic.