How to Upload Files to Amazon S3 with Node.js, Express and AWS SDK v3

Learn how to upload files to Amazon S3 using Node.js, Express, and AWS SDK v3. Step-by-step tutorial with code examples. Build a scalable file upload API.

miércoles, 29 de julio de 2026 • 6 min read • Q2BSTUDIO Team

Guía paso a paso para crear una API de carga de archivos con Node.js y S3

In today's digital ecosystem, file management is a critical component of almost any modern web application. Whether it's uploading profile pictures, PDF documents, invoices, or media files, you need a solution that is scalable, secure, and efficient. Storing files directly on the application server can lead to bottlenecks, unpredictable costs, and data loss risks. This is where Amazon S3 (Simple Storage Service) comes in—an object storage service from AWS that provides 99.999999999% durability and virtually unlimited scalability.

In this article, we will show you how to build a simple yet robust REST API for uploading files directly to an Amazon S3 bucket using Node.js, Express, Multer, and AWS SDK v3. Additionally, we will explore security best practices, cloud service integration, and how companies like Q2BSTUDIO implement this kind of solution in custom software projects for their clients.

This tutorial is aimed at developers who are new to AWS as well as those looking to optimize their file upload processes. Throughout the text, we will reference concepts such as cybersecurity, artificial intelligence (AI), AI agents, and Business Intelligence (BI) with Power BI—all services that Q2BSTUDIO integrates naturally into our developments.

Before you start, make sure you have Node.js 18 or higher, an active AWS account, a created S3 bucket, and an IAM user with programmatic access (Access Key ID and Secret Access Key). If you're not experienced with AWS, don't worry—we'll guide you step by step.

Step 1: Project Setup

Open your terminal and run the following commands to create a new project and install the necessary dependencies:

mkdir s3-upload-apicd s3-upload-apinpm init -ynpm install express multer dotenv uuid @aws-sdk/client-s3

The installed packages are: Express as a web framework, Multer for handling file uploads (in memory), dotenv for environment variables, uuid for generating unique names, and AWS SDK v3 for interacting with S3.

Step 2: Environment Variables

We'll create a .env file with the configuration of our AWS account and bucket. Remember to replace the values with your own credentials. It is essential not to upload this file to public repositories; therefore, we will also create a .gitignore that excludes node_modules and .env.

AWS_REGION=us-east-1AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLEAWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEAWS_BUCKET_NAME=my-upload-bucketPORT=3000

At Q2BSTUDIO, we always recommend managing credentials securely, using services like AWS Secrets Manager or Azure Key Vault when moving to production. This is part of our cybersecurity practices.

Step 3: Configure the S3 Client

In the main application file (e.g., index.js), import the S3Client module and initialize it with the credentials:

import { S3Client } from '@aws-sdk/client-s3';const s3 = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, },});

This configuration works both for AWS buckets and for multicloud environments (Azure/AWS) that we handle at Q2BSTUDIO, where we leverage the strengths of each platform.

Step 4: Configure Multer with Memory Storage

Multer is a Node.js middleware for handling file uploads. We'll use memoryStorage() so that the file is loaded into a buffer and can be sent directly to S3 without temporarily storing it on the server's disk:

import multer from 'multer';const upload = multer({ storage: multer.memoryStorage() });

This approach is ideal for scalable APIs because it avoids local writes and reduces latency. It is also compatible with serverless architectures like AWS Lambda or Azure Functions—services we offer as part of our cloud solutions.

Step 5: Create the Upload Endpoint

Now we define a POST route that receives a file, generates a unique name with UUID, and uploads it to the bucket using the PutObject command:

import { PutObjectCommand } from '@aws-sdk/client-s3';import { v4 as uuid } from 'uuid';

app.post('/upload', upload.single('file'), async (req, res) => { try { const filename = `${uuid()}-${req.file.originalname}`; await s3.send(new PutObjectCommand({ Bucket: process.env.AWS_BUCKET_NAME, Key: filename, Body: req.file.buffer, ContentType: req.file.mimetype, })); res.json({ success: true, filename }); } catch (error) { console.error(error); res.status(500).json({ success: false, message: 'Upload failed' }); }});

This code handles basic errors, but in a production environment we should add file type validation, maximum size, and authentication. At Q2BSTUDIO, when we develop custom software, we include additional security layers such as JWT authentication and role-based access policies.

Step 6: Start the Server

Add the following code to start Express on the configured port:

const express = require('express');const app = express();const PORT = process.env.PORT || 3000;app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Run npm start and your API will be ready at https://localhost:3000.

Step 7: Test with Postman

Open Postman and enter a POST request to https://localhost:3000/upload. In Body, select form-data, add a key called file of type File, and choose any image or document. Upon sending, you will receive a response like:

{ 'success': true, 'filename': '4c6ddcf2-profile.png' }

Verify in your AWS S3 console that the file appears in the bucket.

Common Errors and Solutions

During development, you may encounter errors like AccessDenied: it means your IAM user does not have permission to upload objects. Make sure to attach a policy that includes s3:PutObject. Another typical error is NoSuchBucket: check the bucket name and region. And if you see InvalidAccessKeyId, verify your credentials. These identity management practices are part of the cybersecurity that we implement at Q2BSTUDIO to protect our clients' data.

Next Steps and Enhancements

Once the basic upload works, you can extend the API with additional features:

Multiple File Upload

Modify Multer to accept multiple files with upload.array('files', maxCount) and process each one in a loop.

Pre-signed URLs

Generate temporary URLs so users can upload files directly from the frontend without exposing your credentials. This is useful in applications with React or Angular, and we combine it with AI services to automatically classify uploaded documents.

File Type and Size Validation

Implement a custom middleware that rejects files with dangerous extensions or that exceed a limit (e.g., 10 MB). This is essential for cybersecurity and to avoid S3 cost overruns.

Frontend Integration

Connect this API to a React application using fetch or Axios. At Q2BSTUDIO, we develop custom software with modern stacks like React, Vue, or Angular, always synchronized with the backend.

Using CloudFront for Global Distribution

To accelerate static file delivery, you can place CloudFront in front of your S3 bucket. This reduces latency and improves user experience, especially if your audience is geographically distributed.

Folder Organization

You can structure objects in S3 using prefixes (e.g., users/{id}/images/) to facilitate management and lifecycle policies.

Business Use Cases and the Value of Q2BSTUDIO

At Q2BSTUDIO, we have helped many companies implement document management systems based on S3, combined with Business Intelligence with Power BI to analyze usage patterns, and AI agents that automatically process uploaded files (data extraction, classification, etc.). For example, an insurance company can upload accident reports, and an AI agent extracts key information and sends it to a Power BI dashboard for analysis.

Our cloud experts (AWS and Azure) design scalable and secure architectures tailored to each project's specific needs. Whether you need a simple file upload API or a complex processing system with artificial intelligence, at Q2BSTUDIO we offer custom software development services that integrate these technologies naturally.

Conclusion

Building a file upload API with Node.js and Amazon S3 is straightforward once you understand the basic flow: the client sends the file, Multer captures it in memory, and the AWS SDK transfers it to the bucket. This approach is scalable, secure, and prepares you to integrate advanced features such as pre-signed URLs, validations, and AI-driven automation.

The key is not to limit yourself to simple uploads: think about how your application will handle files after they are uploaded. Artificial intelligence and automation can transform a simple repository into a business engine. At Q2BSTUDIO, we are ready to help you make that leap. Shall we talk about your next project?

A BREAK?

Play for a moment before you go

OUR SERVICES

How we can help you

Do you have a project in mind?

Tell us your vision and we'll turn it into a software solution. Whatever the scope, we make your idea real.