← All case studies

Case Study

Inventory Media Platform

Scaling a media-heavy inventory platform beyond its upload limits

Next.jsTypeScriptNode.jsExpressPrismaMySQLAWS S3AWS ECREC2DockerZodSharp

A media-heavy inventory SaaS used by small teams to catalog physical items had a working Node.js and TypeScript backend, but critical workflows were beginning to buckle under real usage. Large uploads failed unpredictably, admin access was broken, and deployment relied on commands remembered rather than a repeatable process.

I stabilized the platform's authentication, media, and admin workflows while designing a direct-to-S3 upload architecture that removed application servers from the file-transfer path.

The Challenge

The platform needed to:

  • Accept images and videos beyond the platform's 4.5 MB request-body limit
  • Stop proxying every uploaded byte through both the frontend and backend
  • Track incomplete uploads without creating durable media records prematurely
  • Generate thumbnails and clean up abandoned objects predictably
  • Restore admin authorization without changing the database or frontend
  • Align resource, metadata, and media semantics across frontend and backend teams
  • Replace manual AWS deployments with repeatable operational tooling

The work was not one isolated upload endpoint. It crossed browser behavior, API authorization, object storage, relational state, image processing, frontend contracts, and deployment operations — while the existing product remained in use.

What I Built

I hardened the Node.js, TypeScript, Express, Prisma, MySQL, and AWS backend around three critical workflows:

  • A two-phase direct-to-S3 upload protocol using constrained presigned POST policies
  • Pending-upload records tying each authorization to a user, resource, S3 key, MIME type, and expiry
  • Confirmation-time object verification before durable media records are created
  • Normalized 300×300 image thumbnails with predictable derivative keys
  • An explicit placeholder state for video thumbnails, preserving the API contract for later frame extraction
  • Soft-delete and S3 tagging semantics that preserve recovery and lifecycle options
  • Cleanup tooling for expired pending uploads and orphaned S3 objects
  • Corrected JWT role propagation and explicit admin-level authorization rules
  • Type-safe Zod schemas, updated OpenAPI contracts, integration guidance, and operational runbooks
  • Docker, ECR, EC2 deployment, Prisma migration, and health-verification scripts

The result was an upload system the frontend could integrate without routing media through the application stack, plus clearer contracts for the teams maintaining it.

How It Works

The redesigned upload path separates authorization from transfer and persistence:

  1. 01The client requests permission to upload a specific file for a specific resource.
  2. 02The backend validates ownership, MIME type, size, and permissions, creates a pending-upload record, and returns a constrained SigV4 POST policy.
  3. 03The browser uploads the file directly to S3, bypassing the application servers.
  4. 04The client confirms completion using the pending upload identifier.
  5. 05The backend binds the request to the pending record and verifies that the expected object exists before touching durable media state.
  6. 06Images receive a normalized thumbnail; videos receive an explicit placeholder state without changing the client contract.
  7. 07The backend creates the media record, while cleanup tooling handles authorizations that expire without confirmation.

This protocol keeps untrusted file bytes out of the backend request path without trusting the client to declare that an upload succeeded.

Key Engineering Decisions

Separate upload authorization from durable media state

A presigned policy proves that a client may upload; it does not prove that the expected object arrived or belongs in the application's database. Pending-upload records bridge that gap without creating permanent records for abandoned attempts.

Confirmation verifies the expected S3 object before creating the durable media record. The cleanup path can then expire incomplete attempts independently of user-facing media data.

Constrain direct uploads instead of treating S3 as public storage

The project's AWS setup did not include a usable presigned-POST helper, so I implemented the narrowly scoped SigV4 POST policy flow directly. The policy binds uploads to the expected bucket, key, content type, size range, credential scope, and expiry.

This also corrected an earlier signing defect: the policy used the wrong algorithm and omitted the request date, producing cryptic S3 failures rather than a useful application error.

Preserve recovery before deleting storage

Media deletion is soft at the database layer, while S3 objects receive lifecycle tags instead of being removed immediately. That keeps accidental deletion recoverable and lets retention policy remain an operational choice rather than an irreversible API side effect.

Keep future media processing behind a stable contract

Images receive normalized thumbnails immediately. Video records use an explicit placeholder state while preserving the same thumbnail contract, allowing later FFmpeg extraction or asynchronous processing without changing clients or the media schema.

Turn deployment knowledge into executable operations

ECR login, immutable image selection, container restart, environment wiring, migration execution, and basic health checks moved into documented scripts. The goal was not elaborate orchestration; it was removing 'run these commands from memory' from a growing production system.

What Production Revealed

Stabilizing the surrounding system exposed failures that were easy to misdiagnose from the UI alone.

The database role was correct; the token made it impossible

Admin users received 403 responses even though their database records were correct and the middleware behaved as written. The defect sat between them: JWT issuance capped every adminLevel at 3, while authorization required a level of 10 or greater. Every newly issued admin token was structurally incapable of passing the check.

Removing the cap restored access without a database or frontend change. I documented the role ranges, root cause, test procedure, and prevention strategy so the incident could not quietly recur.

Internal workflow states must match the user's mental model

The upload protocol needed explicit pending, confirmed, soft-deleted, and expired states, but frontend teams should not have to infer those semantics from scattered endpoints. Zod schemas, OpenAPI examples, and integration guidance made nulling references, linking metadata, and confirming media predictable at the client boundary.

Observability includes cleanup paths

Direct uploads move bandwidth away from the backend, but they also create a new failure mode: objects can arrive without confirmation. Tracking authorizations by identifier and expiry made abandoned uploads visible and gave cleanup tooling a deterministic scope.

Result

Images and videos up to 100 MB can upload directly to S3 without passing file bodies through the frontend or backend servers, removing the application stack's duplicate transfer of media payloads and the prior 4.5 MB request limit.

Admin access was restored, media gained explicit lifecycle states and recovery paths, frontend teams received a documented contract, and AWS deployments and migrations became repeatable one-command operations instead of a memorized sequence.

What it demonstrates

Secure direct-to-object-storage upload architectureCross-layer authorization debuggingMedia lifecycle and derivative designFrontend-backend contract alignmentAWS deployment and operational toolingIncremental hardening of an in-flight product

This engagement shows how I work inside an existing product: map the real failure paths, repair the highest-trust workflows first, and leave behind interfaces and operational tools that make the next change safer than the last.

Discuss a project like this