Fastify has changed how developers build Node.js web applications. It's fast, easy to use, and handles file uploads well without the usual complexity.
File uploads can be challenging to get right. You can often create security holes or build systems that frustrate users. Fastify provides the tools to build file upload systems that function effectively and remain secure.
This guide demonstrates how to implement file upload functionality that is reliable in production.
Prerequisites
You need Node.js 18 or later installed on your computer. This tutorial assumes you are familiar with JavaScript basics, understand async/await, and have experience building web applications.
Creating your Fastify file upload foundation
Building a good file upload system starts with proper planning. You need a structure that can grow with your app and handle edge cases properly.
Create a new project directory and set up a clean workspace:
Configure your project for ESM modules:
Install the packages you need for file handling:
Here's what each package does:
fastify: The main framework that handles requests and file uploads with great performance.@fastify/multipart: Parses multipart form data, which is how file uploads work on the web.@fastify/static: Serves uploaded files from your server with proper caching.sharp: Processes and optimizes images with professional quality.file-type: Checks file types by looking at the actual file content, not just the extension.
Start with a basic Fastify server to make sure everything works. Create your main application file:
Test your setup before adding more features. Start the server:
Go to http://localhost:3000 in your browser. You should see:
Your Fastify server is working and ready for file upload features.
Implementing basic file upload functionality
Now let's add file upload capabilities to your server. Fastify's plugin system makes this easy while keeping performance high.
First, create a helper module for file operations:
Update your main server file to handle file uploads:
This code uses Node.js streams to handle files efficiently. The request.file() method gives you a stream that you can pipe directly to the file system using pipeline(). This handles errors properly and uses memory efficiently.
Restart your server:
Test your file upload with curl:
You should see a successful response:
Verify the file was actually saved:
You should see your test file listed and its contents displayed. This confirms your basic file upload system works.
Create a second test file for Postman testing:
You can also test your upload using Postman by creating a new POST request to http://localhost:3000/upload/single, going to the Body tab, selecting "form-data", adding a key called "file" with type "File", selecting your test file, and clicking Send.
You should see the same JSON response in Postman's response panel. This visual method makes it easier to test different files and see the responses formatted nicely.
Check your project directory. You should see a new uploads folder with your test file inside. This confirms your basic file upload system works with both command-line tools and GUI applications.
Building file validation systems
Your current upload system accepts any file type and size. This can create security problems and potentially crash your system. Production apps need good validation before accepting uploads.
Let's add basic file extension validation to show how this works:
This validator class checks if uploaded files have acceptable extensions. It creates a list of allowed file types and compares the file extension against this list. If the file doesn't match an allowed extension, it returns validation errors with helpful messages.
Update your main server file to use validation:
First, you imported the randomUUID function from Node's crypto module and your new FileValidator class.
Then you created a validator instance that will check file extensions. In your upload endpoint, you now validate each file before saving it - if validation fails, you return an error message with details about what went wrong.
You also generate unique filenames using randomUUID() to prevent files from overwriting each other, and you return both the original filename and the stored filename in your response.
Restart your server and test with different file types:
Test with a valid file:
Try uploading a file type that's not allowed:
You should get a validation error:
Your validation system now protects against unwanted file types and gives users clear feedback about what went wrong.
Managing multiple file uploads efficiently
Many apps need users to upload multiple files at once. Photo galleries, document collections, and batch processing all benefit from multi-file uploads.
Fastify handles multiple files through the same multipart interface, but you need to process each file separately while keeping the same validation standards.
Add a multiple file upload endpoint to your server:
This endpoint uses request.parts() to process multiple files as they arrive. It validates each file individually, saves the valid ones, and tracks both successful uploads and failures. The system processes files one at a time to use memory efficiently and enforces a maximum file limit to prevent abuse.
Restart your server and test multiple file uploads:
Create several test files:
Test multiple file upload with curl:
You should get a detailed response showing successful uploads and validation failures:
Your multi-file upload system processes each file separately and gives you clear feedback for both successful uploads and validation failures.
Final thoughts
You've built a complete file upload system using Fastify that handles single files, multiple files, and includes validation. Your system validates file extensions, generates unique filenames, and provides clear feedback for uploads and failures.
This foundation is production-ready and can be extended with features like cloud storage or image processing. For more capabilities, check out the Fastify documentation.