intermediate

Object storage

Use object storage for blobs, static assets, backups, signed uploads, lifecycle rules, and immutable durable data.

Object storage (S3, GCS, Azure Blob, R2) stores immutable blobs keyed by path — user uploads, static assets, logs, backups, and CI artifacts. FullStack JS apps usually upload via presigned URLs so browsers send files directly to storage while the API validates metadata and permissions.

					// API mints presigned PUT; client uploads directly to bucket
const url = await s3.getSignedUrl('putObject', {
  Bucket: 'uploads',
  Key: `${userId}/${uuid()}.webp`,
  Expires: 300,
  ContentType: 'image/webp',
});
				

| Control | Purpose | |---------|---------| | Bucket policy / IAM | Who can read/write | | Lifecycle | Expire temp prefixes, tier old logs | | CORS | Browser direct upload rules | | Encryption | SSE-S3, SSE-KMS, or client-side |

Never expose listing on sensitive buckets. Use content-type and size limits at the edge or in Lambda validation. Versioning helps accidental overwrite recovery.

On interviews: presigned URL flow, public vs private buckets, consistency model, CDN in front of static buckets, and cost of egress plus request charges.

Common pitfalls: public `ListBucket`; predictable object keys enabling enumeration; long-lived presigned URLs; storing secrets or DB dumps unencrypted.

The trade-off is durable cheap storage at scale versus eventual consistency listings, no POSIX semantics, and egress costs when serving globally.

Checklist:

  • Presigned uploads with short TTL and typed content.
  • Lifecycle rules for temp and log prefixes.
  • Block public access by default; open only CDN paths.
  • Scan uploads and validate size server-side.