Technical Case Study

Inside the Feels architecture.

Feels is a short-form video platform where users collect clips from TikTok, YouTube Shorts, and X into shareable vertical feeds. The hard part is not the feed. It is the import pipeline: resolving third-party video URLs through a Cloudflare Worker proxy pool without routing large media through the app server. SYZO owned the full build from design through production.

This page is based on the Feels application repository and the separate Feels import worker repository, covering the Next.js app, Drizzle schema, Cloudflare workers, ffmpeg container, R2 storage, Redis engagement layer, and deployment setup.

Feels product page with short-form video app mockups

Who created it

SYZO's founding team owned product design, full-stack development, Cloudflare worker and container architecture, and deployment for Feels from start to production.

How this case study was prepared

Written directly from the Feels application repository and the separate Feels import worker repository, covering schema, storage utilities, worker logic, and deployment guides.

Last reviewed

May 18, 2026, during the E-E-A-T content review for the SYZO website.

Architecture

Four service boundaries keeping a media product manageable.

Browser app

Upload, import, feed, profile, playback

Next.js / tRPC

Auth, validation, presign, publish, feed APIs

Turso + Drizzle

Users, feels, videos, follows, activity

Cloudflare R2

Video objects, thumbnails, public media URLs

Import worker

YouTube Shorts extraction, proxy pool, signed relay

Thumbnail worker

Queue entrypoint for server thumbnail fallback

ffmpeg container

Extracts JPEG frame through signed R2 URLs

Upstash Redis

Rate limits, view dedupe, fanout, top reactions

1. Client talks to typed tRPC routes for product actions.

2. Media moves directly to R2 or through controlled import/thumbnail workers.

3. Turso remains the durable source of truth; Redis accelerates hot paths.

Stack

The moving parts behind the product.

Frontend

Next.js App Router, React 19, tRPC, TanStack Query, Tailwind

Auth

Better Auth with Google, Facebook, and magic-link email through Resend

Data

Turso/libSQL with Drizzle schema and tracked migrations

Media

Cloudflare R2 with presigned uploads and public playback URLs

Import service

Cloudflare Worker, Durable Object proxy pool, Cloudflare Containers, signed relay URLs

Async work

Cloudflare Worker, Cloudflare Queue, and ffmpeg container for thumbnail fallback

Realtime-ish state

Upstash Redis for rate limits, feed fanout, view dedupe, and top reactions

Import Worker

The most complex edge service in the system.

The Feels import worker is its own Cloudflare Worker project. It accepts a URL, validates that the current implementation can process it, and then uses YouTube watch-page parsing, Innertube player attempts, signature deciphering, and a proxy backed relay flow to return a playable media URL.

To keep imports resilient, it stores proxy health in a Durable Object, prefers known-working proxies, bans failing proxies for a cooldown period, and starts Cloudflare Containers configured with the selected SOCKS proxy. The relay URL is signed with an HMAC token, has a short TTL, and refreshes the upstream media URL before forwarding playback requests.

Proxy state

A Durable Object tracks working, new, and temporarily banned proxies.

Container fetch

Each selected proxy is passed into a Cloudflare Container running a small Node fetch relay.

Debuggability

Trace IDs and optional debug payloads expose failures without leaking direct stream URLs.

Implementation

Decisions made to keep production work predictable.

01

Direct media uploads without routing video through the app server

The upload flow starts in the Next.js app, but large video files do not pass through a Next.js route. The app asks the backend for short-lived R2 presigned PUT URLs, the browser uploads the video and thumbnail directly to R2, and the backend only finalizes database records after the client reports a successful upload.

  • Object keys are scoped under users/{userId}/feels/{feelId}/ so finalize can reject keys that do not belong to the current user and feel.
  • The server validates content type, upload size intent, draft state, and slug rules before publishing.
  • Draft rows are created during finalize, which reduces abandoned database records when uploads fail midway.

02

Imported clips are treated as a controlled ingestion pipeline

Feels delegates social URL resolution to a separate Cloudflare import worker. The app requests a preview or resolved media URL, then validates and stores the resulting media itself. Original URLs and content hashes are checked so repeated imports can reuse existing video records instead of duplicating storage.

  • The app has source detection for YouTube Shorts, X/Twitter status videos, TikTok videos, and Instagram Reels; the inspected import worker currently implements the YouTube Shorts path.
  • Resolved video and thumbnail assets are downloaded with byte limits and content-type checks before being written to R2.
  • The worker uses a Durable Object to track proxy health, starts proxy-fetch containers per selected proxy, signs relay URLs, and retries across proxy candidates when YouTube blocks a request path.

03

The data model separates media, posts, and ordering

The database keeps users, videos, feels, and feel-to-video ordering separate. This lets one stored video record be linked into a feel, while the feel itself owns title, slug, draft/publish status, reaction counts, view counts, and deletion state.

  • Drizzle migrations define indexed tables for users, follows, videos, feels, feel_videos, reactions, bookmarks, and activity.
  • Unique hashes help deduplicate imported URLs and video content.
  • Published feels get slug allocation with conflict handling and suggestions instead of silent overwrites.

04

Feed and engagement use the database as source of truth with Redis accelerators

The feed can fall back to database queries, but signed-in users can also receive Redis-backed fanout IDs. Engagement mutations update durable database rows while Redis handles fast paths such as rate limiting, daily view dedupe, and top-reaction counts.

  • Reactions and bookmarks are protected mutations with Upstash sliding-window rate limits.
  • View counting uses a Redis SET NX key per feel, viewer, and day when Redis is available.
  • Top reactions are read from a Redis sorted set, while total counts remain stored on the feel row.

05

Thumbnail generation has a fallback path for unreliable mobile decoding

Client-side thumbnail extraction can fail on some browsers and codecs, so Feels has a Cloudflare-backed fallback. The app enqueues a job, a Cloudflare Queue consumer calls an ffmpeg container, and the container asks the app for short-lived R2 URLs before writing a generated JPEG and committing the new thumbnail URL.

  • Worker and container calls are protected with shared Cloudflare secrets.
  • Trace IDs move through the app, worker, and container so failed thumbnail jobs can be followed across services.
  • The container tries multiple seek positions and scales generated JPEGs to a bounded width before upload.

06

Operational work is captured alongside the code

The repo keeps deployment and maintenance material close to the system it describes. Production guides document Vercel environment scopes, OAuth redirect setup, Cloudflare worker variables, R2 configuration, Turso migrations, and security checks.

  • Environment validation is centralized with t3 env and Zod so missing production services fail early.
  • Database migration scripts cover normal Drizzle migrations plus targeted Turso migration workflows.
  • Security notes record current tradeoffs, including public-read media playback and the plan for private bucket or signed playback hardening.

Maintenance

How the complexity stays manageable.

Boundaries

Next.js owns product UI, auth, tRPC, and database writes; Cloudflare owns async thumbnail work; R2 owns media storage.

Validation

Inputs are parsed with Zod, object keys are checked for ownership, and import payloads are validated before storage.

Secrets

App-to-worker calls use shared Cloudflare headers, while OAuth, R2, Turso, Redis, and Resend credentials stay in environment variables.

Recovery

Import and thumbnail paths include trace IDs, fallback behavior, and clear failure messages so support work starts from an observable request.

Result

In production, with 100+ daily users.

Feels has more than 100 daily active users, a Cloudflare-backed import pipeline that resolves and proxies third-party video URLs, direct-to-R2 uploads that bypass the app server, and a Redis engagement layer that handles view deduplication, rate limits, and feed fanout without touching the database on every request.

Because the service boundaries are explicit and the operational notes live with the code, SYZO can continue to extend and operate the product without rediscovering how each part works.