AskHandle

AskHandle Blog

NodeJS and AWS S3: A Quick Guide to File Storage

December 20, 2025Katherine Holland3 min read

NodeJS and AWS S3: A Quick Guide to File Storage

Managing file storage in web applications can often feel complex, but integrating AWS S3 with NodeJS provides a streamlined and powerful solution. This guide offers practical insights and tips based on my experience, showcasing how these tools can simplify your workflow.

Setting Up Your Project

Let's start by installing the necessary AWS SDK for NodeJS. Open your terminal and execute:

bash
1npm install @aws-sdk/client-s3

Next, you'll need to configure your AWS credentials. It's crucial to keep these secure and never commit them directly to your repository. A best practice is to utilize environment variables, as shown below:

javascript
1const { S3Client } = require('@aws-sdk/client-s3');
2
3const s3Client = new S3Client({
4  region: process.env.AWS_REGION,
5  credentials: {
6    accessKeyId: process.env.AWS_ACCESS_KEY,
7    secretAccessKey: process.env.AWS_SECRET_KEY
8  }
9});

Uploading Files to S3

Uploading files to S3 is a straightforward process. You'll need the file content (as a buffer or stream) and a unique key (filename) to identify it in your bucket. Here's how:

javascript
1const { PutObjectCommand } = require('@aws-sdk/client-s3');
2
3async function uploadFile(fileBuffer, fileName) {
4  const params = {
5    Bucket: 'your-bucket-name',  // IMPORTANT: Replace with your actual bucket name
6    Key: fileName,
7    Body: fileBuffer
8  };
9
10  try {
11    await s3Client.send(new PutObjectCommand(params));
12   const fileUrl = `https://your-bucket-name.s3.amazonaws.com/${fileName}`; // Correcting the URL and adding the filename
13    return fileUrl;
14  } catch (error) {
15    console.error('Upload failed:', error);
16    throw error;
17  }
18}

Downloading Files from S3

Fetching files from S3 is equally easy. You can retrieve the data as a stream or a buffer, as demonstrated below:

javascript
1const { GetObjectCommand } = require('@aws-sdk/client-s3');
2
3async function getFile(fileName) {
4  const params = {
5    Bucket: 'your-bucket-name', // IMPORTANT: Replace with your actual bucket name
6    Key: fileName
7  };
8
9  try {
10    const data = await s3Client.send(new GetObjectCommand(params));
11    return data.Body; // Returns the body stream
12  } catch (error) {
13    console.error('Download failed:', error);
14    throw error;
15  }
16}

Managing File Access with Presigned URLs

S3 provides flexible control over file access. You can grant public access, keep files private, or create temporary access links (presigned URLs) for secure sharing. This example shows how to generate a presigned URL:

javascript
1const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
2
3async function getTemporaryLink(fileName) {
4  const command = new GetObjectCommand({
5    Bucket: 'your-bucket-name', // IMPORTANT: Replace with your actual bucket name
6    Key: fileName
7  });
8
9  return await getSignedUrl(s3Client, command, { expiresIn: 3600 }); // Link valid for 1 hour (3600 seconds)
10}

Error Handling and Robustness

When working with cloud storage, it's vital to handle errors gracefully. Here are some common pitfalls and how to manage them:

  1. Network Timeouts: Especially during large uploads, network issues can cause failures.
  2. File Size Limits: S3 has size limits that you should consider and implement in your application.
  3. Bucket Permissions: Make sure your application has correct access to the desired S3 bucket.
  4. Invalid File Types: Handle files based on their MIME type to prevent unexpected errors.

Here's an improved safeUpload function demonstrating some error handling:

javascript
1async function safeUpload(fileBuffer, fileName, maxSize = 5000000) {
2  if (fileBuffer.length > maxSize) {
3    throw new Error('File too large');
4  }
5
6  try {
7    const fileUrl = await uploadFile(fileBuffer, fileName);
8    return fileUrl; // returning file URL
9  } catch (error) {
10    if (error.code === 'NetworkingError') {
11        console.error("Network error during upload, consider retry logic", error)
12      // Implement retry logic here
13    }
14    console.error("Error during upload", error)
15    throw error;
16  }
17}

Performance Optimization Best Practices

To optimize the performance of your S3 integration, consider the following:

  • Utilize Streams: Process large files by using streams to minimize memory usage.
  • Implement Retry Logic: Handle transient errors by using retry logic to ensure reliable uploads/downloads.
  • Caching: Cache frequently accessed files to reduce latency.
  • Proper File Naming: Adopt meaningful naming patterns for easy management.
  • CDN for Delivery: Improve content delivery speed using a CDN like CloudFront.

Monitoring and Maintenance

Keeping an eye on your S3 usage is essential. Consider tracking your bucket size and implementing proper clean-up strategies to control storage costs:

javascript
1const { ListObjectsCommand } = require('@aws-sdk/client-s3');
2
3async function checkBucketSize() {
4  try{
5      const data = await s3Client.send(new ListObjectsCommand({
6        Bucket: 'your-bucket-name' // IMPORTANT: Replace with your actual bucket name
7      }));
8
9      const totalSize = data.Contents ? data.Contents.reduce((total, file) => total + file.Size, 0) : 0;
10     return totalSize;
11
12  }catch(error){
13    console.error("Error getting bucket size", error)
14    throw error
15  }
16}
17

This guide provides a starting point for integrating AWS S3 with NodeJS. You can adapt these examples for your specific use cases and explore the full potential of the AWS SDK.