Back

Introduction#

NewTube is a YouTube-style full-stack project built with Next.js 15, React, tRPC, Drizzle, Clerk, Mux and related managed services. This article focuses on how the repository is organized and how requests move through the application.

GitHub Repository: NewTube Clone ↗

Source and setup instructions: NewTube Clone on GitHub ↗. A hosted demo is not currently published.


Project Overview#

Implemented surface#

NewTube implements the following repository features:

  • Video upload and HLS playback through Mux
  • Clerk-backed sign-in and user mapping
  • Creator screens for video management
  • Creator subscriptions
  • Comments and reactions
  • Playlists
  • Hooks for generating titles, descriptions, and thumbnails
  • Responsive layouts for desktop and mobile

Tech stack#

LayerTechnology
FrontendNext.js 15, React 19, TypeScript, Tailwind CSS
BackendtRPC, Drizzle ORM
DatabasePostgreSQL (Neon)
AuthClerk
VideoMux
StorageUploadThing
CacheUpstash Redis
QueueQStash

Architecture#

System architecture#

The repository separates the browser and React layer, the tRPC API layer, and data or external-service access. The important boundary is that UI code calls typed procedures instead of reaching directly into database or service clients.

flowchart TB
    U[Browser and React UI] --> T[tRPC client]
    T --> A[tRPC procedures]
    A --> V[Zod validation and auth checks]
    V --> D[Drizzle and PostgreSQL]
    V --> M[Mux and other external services]
    V --> C[Redis and background services]
mermaid

Project Structure#

I organized the codebase using a feature-based module structure :

modules/
├── videos/
│   ├── server/
│   │   └── procedures.ts    # tRPC API endpoints
│   ├── ui/
│   │   └── components/      # React components
│   └── type.ts              # TypeScript types
├── comments/
├── subscriptions/
├── playlists/
└── ... (other features)
plaintext

This layout keeps server procedures, UI code, and feature types close to the feature that uses them.

Request Flow Example#

Here’s how a typical API request flows through the system:


Core Technologies and Features#

1. Server Components with SSR Prefetching#

Next.js 15’s App Router allows us to prefetch data on the server and hydrate it to the client:

Benefits:

  • Avoids a separate client fetch for data that was prefetched on the server
  • Better SEO with server-rendered content
  • Reduced client-side JavaScript

2. Type-Safe API Layer with tRPC#

tRPC provides end-to-end type safety without code generation:

3. Authentication Middleware with Caching#

The authentication flow is optimized with a three-tier caching strategy:

4. Video Processing Pipeline#

The video upload and processing flow uses webhooks for async updates:

User Upload → Mux Upload URL → Mux Processing → Webhook → Database Update
     │              │                │               │            │
     └──────────────┴────────────────┴───────────────┴────────────┘
                    Asynchronous flow with real-time updates
plaintext

5. AI-assisted metadata generation#

Using QStash for async AI tasks:

6. Optimistic updates and rollback#


Problems encountered#

Challenge 1: SSR Authentication with Protected Routes#

Problem: When using SSR with protected routes, the server doesn’t have access to the client’s authentication state, causing 401 errors or hydration mismatches.

Solution: Implement a multi-layered approach:

Key Insight: Using useRef prevents the sign-in modal from opening multiple times during React’s strict mode double-render.

Challenge 2: Hydration Mismatch in Mobile Detection#

Problem: A mobile detection hook caused hydration errors because the initial value differed between server and client.

// ❌ Problem: undefined on server, boolean on client
const [isMobile, setIsMobile] = useState<boolean | undefined>(undefined);
return !!isMobile;
typescript

Solution: Default to false and only update on client:

// ✅ Fixed: Consistent initial value
const [isMobile, setIsMobile] = useState<boolean>(false);

useEffect(() => {
	const checkMobile = () => {
		setIsMobile(window.innerWidth < 768);
	};

	checkMobile();
	window.addEventListener('resize', checkMobile);
	return () => window.removeEventListener('resize', checkMobile);
}, []);

return isMobile;
typescript

Challenge 3: Cache Invalidation Across Features#

Problem: When a user likes a video, the “Liked Videos” playlist wasn’t updating in real-time.

Solution: Invalidate all related queries:

const likeMutation = trpc.videoReactions.like.useMutation({
	onSuccess: () => {
		// Primary data
		utils.videos.getOne.invalidate({ id: videoId });

		// Related features
		utils.playlists.getLiked.invalidate();
		utils.playlists.getLikedPreview.invalidate();
	}
});
typescript

Challenge 4: CORS Configuration for Video Uploads#

Problem: In production, Mux uploads failed due to CORS restrictions.

Solution: Use environment-based CORS configuration:

const upload = await mux.video.uploads.create({
	new_asset_settings: {
		/* ... */
	},
	// Development: "*" | Production: specific domain
	cors_origin: process.env.MUX_CORS_ORIGIN || '*'
});
typescript

Challenge 5: Real-time Video Processing Status#

Problem: Users couldn’t see when their video finished processing.

Solution: Implement polling with automatic cleanup:


What the repository demonstrates#

The useful part of this project is the set of boundaries it makes explicit. React components do not query PostgreSQL directly. tRPC procedures own input validation and authorization. Mux handles video processing outside the request path. Client mutations can update optimistically, but they also keep rollback and invalidation paths.

The repository also shows several limits that still need production verification. A hosted deployment is not published, so this article does not claim measured scale, latency, cache hit rate, or SEO improvement. The three-tier auth cache, polling loop, and CORS configuration should be treated as implementation choices to test under the deployment that actually uses them.

Next work#

The next useful steps are concrete: publish a hosted environment, measure the video-processing path, replace polling where event delivery is available, add failure tests around upload and auth boundaries, and document which AI metadata features are enabled in production.

Resources#

End of article · Back to writing

Comments

Comments are public and managed through GitHub Discussions.

Preparing the comment section…

Sign in with GitHub once the comment box is ready, or open the repository discussions directly. Open GitHub Discussions ↗

Comments are published to the public discussions of the lora-sys/loraSys repository.

Other contact methods