DEV Community

Cover image for How I built a high-performance Chinese learning platform with 100k+ TV clips using Rust and Next.js 15
Vincent
Vincent

Posted on

How I built a high-performance Chinese learning platform with 100k+ TV clips using Rust and Next.js 15

Hi dev.to community! 👋

Today, I’m incredibly excited to share MandarinClips, a side project I’ve been pouring my soul into. It's a high-performance language learning platform that indexes and organizes sentence-sized dialogue segments from authentic Chinese movies and TV series to help language learners transition from sterile textbooks to real-world immersion.

👉 Check it out here: MandarinClips.com

Here is a deep dive into the engineering decisions, performance bottlenecks, and the technical architecture that powers the platform under the hood.


🏗️ The High-Level Architecture

I wanted the platform to be blindingly fast, cost-effective, and highly available. Here is the stack:

  • Frontend: Next.js 15 (React 19) + Tailwind CSS + next-intl (for multilingual support).
  • Backend: Rust (Axum framework) + Tokio async runtime.
  • Database: PostgreSQL with pg_trgm (trigram) extension.
  • Storage & CDN: Backblaze B2 proxied through Cloudflare CDN (S3 API compatible, 0-cost egress).

⚡ Backend: Why Rust & Axum?

The backend is responsible for high-frequency search requests, serving sitemaps, and tracking user quota usage.

Using Rust was a no-brainer for several reasons:

  1. Safety & Zero-Cost Abstractions: No garbage collection means zero GC pauses, resulting in extremely predictable latency.
  2. Axum & Tokio: Axum is a highly ergonomic, thin routing wrapper on top of Tokio. It handles asynchronous tasks with minimal CPU overhead, allowing our single 2-core VPS to handle thousands of concurrent queries without breaking a sweat.

🗄️ Database: Sub-millisecond Fuzzy Search with PG GIN Indexes

With over 100,000 video clips and subtitles indexed, a standard ILIKE '%query%' search would quickly degrade and result in full table scans, locking up the CPU under high traffic.

To solve this, we utilized the PostgreSQL pg_trgm (trigram) extension to build GIN (Generalized Inverted Index) on both Chinese characters and no-tone pinyin strings.

CREATE EXTENSION IF NOT EXISTS pg_trgm;

CREATE INDEX idx_clips_text_zh_trgm 
ON clips USING GIN (text_zh gin_trgm_ops);

CREATE INDEX idx_clips_pinyin_search_trgm 
ON clips USING GIN (pinyin_search gin_trgm_ops);
Enter fullscreen mode Exit fullscreen mode

By querying against these indices, fuzzy matches for queries like "ni hao" or "明天见" take under 1 millisecond locally. We achieve pure O(1) list scans.
🎥 Media Delivery: 0-Cost Egress with Backblaze B2 & Cloudflare
Hosting over 100,000 MP4 video clips can quickly become a bandwidth billing nightmare on standard cloud providers.
We solved this by storing all .mp4 video files and .jpg posters in Backblaze B2, which is part of the Cloudflare Bandwidth Alliance. By proxying the B2 bucket through Cloudflare's global edge network:
All public egress bandwidth is $0.
We configured aggressive Cloudflare Cache Rules (using Cache-Control: public, max-age=86400, s-maxage=604800 headers) so that sitemaps and video assets are served straight from Cloudflare's edge memory, dropping origin VPS bandwidth and IOPS to near-zero.
🌐 Frontend: Next.js 15 & Native 308 Permanent Redirections
For the frontend, we used Next.js 15. One of our recent refactoring milestones was migrating legacy URLs from underscores (_) to SEO-friendly dashes (-).
To preserve SEO link juice, we avoided heavy JS-based Edge middleware and leveraged Next.js's native redirects config in next.config.ts to issue 308 Permanent Redirects at the router level:
// next.config.ts
async redirects() {
return [
{
source: '/sitemap.xml',
destination: 'https://www.mandarinclips.com/sitemaps/sitemap.xml',
permanent: true,
}
];
}
This is processed natively before any middleware execution, ensuring zero-latency transitions for Googlebot.
🎁 Wrapping Up & Feedback
Building MandarinClips has been an incredible full-stack journey.
I’d love to hear your feedback on the architecture, the Rust Axum setup, or the database indexing! If you're currently learning Chinese (or are just curious about the UI), please check out MandarinClips and let me know your thoughts in the comments below!
Happy hacking! 🚀

Top comments (1)

Collapse
 
merbayerp profile image
Mustafa ERBAY

This is a really nice architecture, especially the combination of Rust + Axum, PostgreSQL, and Cloudflare/B2 for keeping infrastructure simple and cost-effective. Using pg_trgm for Chinese text and pinyin search is a smart choice.

One small technical note: the article mentions achieving “pure O(1) list scans” with GIN indexes. I’d be a bit careful with that wording. GIN indexes can dramatically reduce search time and avoid full table scans, but trigram similarity searches are not literally O(1). Actual performance still depends on index selectivity, the query, the planner, and the number of matching rows. The practical result—sub-millisecond searches—is the important metric.

I’d also be interested in seeing how the system behaves under concurrent search traffic, cache hit ratios, and index maintenance as the dataset continues to grow beyond 100k clips. Great project!