Lesson 27 of 49 intermediate

AWS S3 — File Storage

Your Files in the Cloud

Open interactive version (quiz + challenge)

Real-world analogy

S3 is like a magical storage locker with unlimited space. You give each file a unique key (like a locker number), and S3 stores it safely in the cloud. Need it back? Just ask for it by key. It never runs out of space and it never loses your files!

What is it?

Amazon S3 (Simple Storage Service) is cloud-based object storage. It's where you store files like images, documents, videos, and backups. Files are organized in buckets and accessed via unique keys.

Real-world relevance

Netflix stores all their content on S3. Airbnb stores listing photos. Almost every app that handles file uploads uses S3 or a similar service (Google Cloud Storage, Azure Blob).

Key points

Code example

// 1. Upload a file to S3 📤
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({ region: 'us-east-1' });

async function uploadFile(file: Buffer, fileName: string) {
  await s3.send(new PutObjectCommand({
    Bucket: 'my-app-uploads',
    Key: `uploads/${Date.now()}-${fileName}`,
    Body: file,
    ContentType: 'image/jpeg',
  }));
}

// 2. Generate a pre-signed URL (temporary access) 🔗
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

async function getUploadUrl(fileName: string) {
  const command = new PutObjectCommand({
    Bucket: 'my-app-uploads',
    Key: `uploads/${fileName}`,
  });

  // URL valid for 15 minutes — then it expires!
  const url = await getSignedUrl(s3, command, { expiresIn: 900 });
  return url; // Frontend uses this URL to upload directly!
}

// 3. NestJS file upload endpoint 📁
@Controller('files')
export class FileController {
  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  async upload(@UploadedFile() file: Express.Multer.File) {
    const key = `uploads/${Date.now()}-${file.originalname}`;

    await this.s3Service.upload({
      bucket: 'my-app-uploads',
      key,
      body: file.buffer,
      contentType: file.mimetype,
    });

    return { url: `https://my-app-uploads.s3.amazonaws.com/${key}` };
  }
}

Line-by-line walkthrough

  1. 1. 1. Upload a file to S3 📤
  2. 2. Importing required dependencies
  3. 3.
  4. 4. Declaring a variable
  5. 5.
  6. 6. Declaring a function
  7. 7. Waiting for an async operation to complete
  8. 8.
  9. 9.
  10. 10.
  11. 11.
  12. 12.
  13. 13. Closing block
  14. 14.
  15. 15. 2. Generate a pre-signed URL (temporary access) 🔗
  16. 16. Importing required dependencies
  17. 17.
  18. 18. Declaring a function
  19. 19. Declaring a variable
  20. 20.
  21. 21.
  22. 22.
  23. 23.
  24. 24. URL valid for 15 minutes — then it expires!
  25. 25. Declaring a variable
  26. 26. Returning a value
  27. 27. Closing block
  28. 28.
  29. 29. 3. NestJS file upload endpoint 📁
  30. 30. Decorator that adds metadata or behavior
  31. 31. Exporting for use in other files
  32. 32. Decorator that adds metadata or behavior
  33. 33. Decorator that adds metadata or behavior
  34. 34.
  35. 35. Declaring a variable
  36. 36.
  37. 37. Waiting for an async operation to complete
  38. 38.
  39. 39.
  40. 40.
  41. 41.
  42. 42.
  43. 43.
  44. 44. Returning a value
  45. 45. Closing block
  46. 46. Closing block

Spot the bug

@Post('upload')
async upload(@UploadedFile() file: Express.Multer.File) {
  const key = file.originalname;
  await this.s3.upload({ Bucket: 'my-bucket', Key: key, Body: file.buffer });
  return { url: 'https://my-bucket.s3.amazonaws.com/' + key };
}
Need a hint?
What happens when two users upload files with the same name?
Show answer
Using file.originalname as the S3 key means files with the same name overwrite each other. Fix: add a unique prefix: const key = Date.now() + '-' + file.originalname;

Explain like I'm 5

S3 is like a magical closet in the sky with infinite space. You can put anything in it - photos, videos, homework. Each thing gets a special label so you can find it later. The closet never runs out of room and never loses your stuff!

Fun fact

Amazon S3 stores over 100 TRILLION objects (files). If each object was a grain of sand, you could fill about 1,000 Olympic swimming pools! 🏊

Hands-on challenge

Create a free AWS account, make an S3 bucket, and upload a file using the AWS console. Then try generating a pre-signed URL using the AWS CLI!

More resources

Open interactive version (quiz + challenge) ← Back to course: Full-Stack Playbook