| Metric | Value |
|---|---|
| Total SLOC | 8,160 |
| Source Files | 58 |
| .js | 3,402 |
| .md | 2,374 |
| .tsx | 1,379 |
| .ts | 593 |
| .sql | 204 |
A typeahead/autocomplete system demonstrating prefix matching, ranking suggestions, and real-time updates. This educational project focuses on building a low-latency suggestion service used by search engines and applications.
- Trie-based data structure with O(prefix length) lookups
- Pre-computed top-k suggestions at each node
- Character-by-character suggestions
- Multi-factor scoring: popularity, recency, personalization, trending
- Configurable weight factors
- Real-time trending detection
- Query log aggregation with buffered writes
- Sliding window counters for trending
- Automatic trie updates
- Redis caching with short TTL for freshness
- Automatic cache invalidation on updates
┌─────────────────┐
│ Frontend │
│ (React + TS) │
└────────┬────────┘
│
┌────────▼────────┐
│ API Gateway │
│ (Express) │
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ Suggestion │ │ Ranking │ │ Aggregation │
│ Service │ │ Service │ │ Service │
│ (Trie-based) │ │ (Multi-factor) │ │ (Query logs) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌────────▼────────┐ ┌──▼───┐ ┌───────▼───────┐
│ PostgreSQL │ │Redis │ │ In-Memory │
│ (Analytics) │ │(Cache)│ │ Trie │
└─────────────────┘ └──────┘ └───────────────┘
- Frontend: TypeScript + Vite + React 19 + TanStack Router + Zustand + Tailwind CSS
- Backend: Node.js + Express
- Databases:
- PostgreSQL (query logs, phrase counts, analytics)
- Redis (caching, trending, user history)
- Data Structure: Custom Trie with pre-computed top-k suggestions
- Node.js 20+
- Docker and Docker Compose
- npm or yarn
-
Start infrastructure:
docker-compose up -d
-
Install backend dependencies:
cd backend npm install -
Seed the database with sample data:
npm run seed
-
Start the backend:
npm run dev
-
Install frontend dependencies (in a new terminal):
cd frontend npm install -
Start the frontend:
npm run dev
-
Open the app:
- Frontend: http://localhost:5173
- Backend API: http://localhost:3000
- Health check: http://localhost:3000/health
If you prefer to run PostgreSQL and Redis natively:
-
Install PostgreSQL:
# macOS brew install postgresql@16 brew services start postgresql@16 # Create database createdb typeahead psql typeahead -f backend/init.sql
-
Install Redis:
# macOS brew install redis brew services start redis -
Set environment variables:
export PG_HOST=localhost export PG_PORT=5432 export PG_USER=your_user export PG_PASSWORD=your_password export PG_DATABASE=typeahead export REDIS_HOST=localhost export REDIS_PORT=6379
-
Follow steps 2-7 from Option 1.
For testing distributed behavior:
# Terminal 1
npm run dev:server1 # Port 3001
# Terminal 2
npm run dev:server2 # Port 3002
# Terminal 3
npm run dev:server3 # Port 3003| Endpoint | Method | Description |
|---|---|---|
/api/v1/suggestions?q=<prefix> |
GET | Get autocomplete suggestions |
/api/v1/suggestions/log |
POST | Log a completed search |
/api/v1/suggestions/trending |
GET | Get trending queries |
/api/v1/suggestions/popular |
GET | Get most popular queries |
/api/v1/suggestions/history?userId=<id> |
GET | Get user's search history |
| Endpoint | Method | Description |
|---|---|---|
/api/v1/analytics/summary |
GET | Get analytics summary |
/api/v1/analytics/queries |
GET | Get recent queries |
/api/v1/analytics/top-phrases |
GET | Get top phrases |
/api/v1/analytics/hourly |
GET | Get hourly query volume |
| Endpoint | Method | Description |
|---|---|---|
/api/v1/admin/status |
GET | Get system status |
/api/v1/admin/trie/stats |
GET | Get trie statistics |
/api/v1/admin/trie/rebuild |
POST | Rebuild trie from database |
/api/v1/admin/phrases |
POST | Add a phrase |
/api/v1/admin/filter |
POST | Filter a phrase |
/api/v1/admin/cache/clear |
POST | Clear suggestion cache |
curl "http://localhost:3000/api/v1/suggestions?q=java&limit=5"Response:
{
"prefix": "java",
"suggestions": [
{ "phrase": "javascript", "count": 50000, "score": 0.85 },
{ "phrase": "javascript tutorial", "count": 35000, "score": 0.78 },
{ "phrase": "java", "count": 45000, "score": 0.75 }
],
"meta": {
"count": 3,
"responseTimeMs": 12,
"cached": false
}
}curl -X POST "http://localhost:3000/api/v1/suggestions/log" \
-H "Content-Type: application/json" \
-d '{"query": "javascript tutorial"}'The trie stores pre-computed top-k suggestions at each node, enabling O(prefix length) lookups:
root
├── j
│ ├── a
│ │ ├── v
│ │ │ ├── a [java, java spring, ...]
│ │ │ └── s [javascript, javascript tutorial, ...]
Final score = weighted sum of:
- Popularity (30%): log10(count)
- Recency (15%): exponential decay over 1 week
- Personalization (25%): user history match
- Trending (20%): real-time trending boost
- Match quality (10%): prefix match quality
- Query received -> Buffer incremented
- Every 30 seconds -> Flush to database + update trie
- Sliding window counters -> Real-time trending
- Periodic decay -> Trending score decay
npm run dev # Start with hot reload
npm run seed # Seed sample data
npm run dev:server1 # Run on port 3001npm run dev # Start dev server
npm run build # Build for production
npm run type-check # TypeScript check
npm run lint # ESLintSee architecture.md for detailed system design documentation.
See CLAUDE.md for development insights and design decisions.
- How We Built Prefixy - Facebook's typeahead system serving billions of queries
- Trie Data Structure - Fundamental data structure for prefix matching
- Autocomplete at Scale - Google Tech Talk on building autocomplete systems
- Design Autocomplete System - System design walkthrough for typeahead
- Elasticsearch Suggesters - Elasticsearch's built-in autocomplete functionality
- Ternary Search Trees - Memory-efficient alternative to tries for prefix matching
- Prefix Hash Tree - Distributed data structure for prefix queries at scale
- LinkedIn Typeahead - How LinkedIn powers typeahead search with specialized indices