01 — Tech Stack
Dokumen ini menjelaskan semua teknologi yang digunakan untuk membangun backend ELJoy, lengkap dengan alasan pemilihan dan peran masing-masing.
Ringkasan Stack
┌─────────────────────────────────────────────────────────────┐
│ CLIENT (Frontend) │
│ React 19 + Vite + TypeScript │
└────────────────────────┬────────────────────────────────────┘
│ HTTPS / SSE
▼
┌─────────────────────────────────────────────────────────────┐
│ API GATEWAY / CDN │
│ Cloudflare (Proxy) │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ APPLICATION SERVER │
│ Hono.js + Node.js + TypeScript │
├─────────────┬───────────────┬───────────────┬───────────────┤
│ Drizzle │ Better Auth │ Vercel AI │ BullMQ │
│ ORM │ │ SDK │ (Queue) │
└──────┬──────┴───────┬───────┴───────┬───────┴───────┬───────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│PostgreSQL│ │ Redis/Upstash│ │ OpenAI / │ │ Cloud │
│ │ │ │ │ Google AI │ │ Storage │
│(Neon/ │ │(Cache, │ │ │ │ (R2/S3) │
│ Supabase)│ │ Session, │ │ Deepgram │ │ │
│ │ │ Rate Limit) │ │ (STT/TTS) │ │ │
└──────────┘ └──────────────┘ └──────────────┘ └──────────┘
Detail Per Layer
1. Runtime & Bahasa
| Teknologi | Versi | Peran |
|---|---|---|
| Node.js | v22 LTS | Runtime server. Dipilih karena satu bahasa dengan frontend (TypeScript), ecosystem NPM terbesar, dan performa async I/O yang sangat baik untuk aplikasi real-time. |
| TypeScript | 5.x | Type safety end-to-end. Mengurangi bug runtime, meningkatkan DX (Developer Experience), dan memudahkan kolaborasi tim. |
Kenapa Node.js, bukan Go/Python/Java?
- Satu bahasa (TypeScript) untuk FE dan BE → tim lebih efisien
- Ecosystem library untuk AI, audio, dan real-time sudah sangat matang
- Non-blocking I/O cocok untuk SSE streaming dan concurrent voice sessions
- Startup time cepat, memory footprint ringan untuk deployment cloud
2. Web Framework
| Teknologi | Versi | Peran |
|---|---|---|
| Hono.js | 4.x | Framework web utama. Ultra-ringan (~14KB), TypeScript-first, multi-runtime (Node.js, Bun, Cloudflare Workers, Deno). |
Kenapa Hono, bukan Express/Fastify/NestJS?
| Kriteria | Express | Fastify | NestJS | Hono ✅ |
|---|---|---|---|---|
| Ukuran bundle | ~200KB | ~100KB | ~2MB+ | ~14KB |
| TypeScript native | ❌ (perlu setup) | ⚠️ (partial) | ✅ | ✅ |
| Performance | Lambat | Cepat | Medium | Sangat cepat |
| Learning curve | Rendah | Rendah | Tinggi | Rendah |
| Edge-ready | ❌ | ❌ | ❌ | ✅ |
| Middleware ecosystem | Sangat banyak | Banyak | Built-in | Banyak |
Fitur Hono yang kita pakai:
- Built-in middleware: CORS, JWT verification, rate limiting
hono/streaming— SSE streaming untuk AI responsehono/validator— Request validation dengan Zod- RPC client — Type-safe API client yang bisa di-share ke frontend
- Multi-runtime — Bisa deploy ke VPS, serverless, atau edge
3. Database
| Teknologi | Versi | Peran |
|---|---|---|
| PostgreSQL | 16+ | Database utama (relational). Menyimpan semua data terstruktur: user, assessment, learning journey, content, membership. |
| Drizzle ORM | 0.35+ | ORM TypeScript yang type-safe, SQL-like syntax, zero overhead, dan migration bawaan. |
Kenapa PostgreSQL?
- JSONB support — Cocok untuk menyimpan data semi-terstruktur (AI evaluation results, learner profile metrics)
- Full-text search — Pencarian konten tanpa perlu Elasticsearch di awal
- Array & Enum types — Efisien untuk tags, CEFR levels, mode types
- Reliable & mature — Battle-tested, excellent tooling
- Managed options — Neon, Supabase, atau Railway menyediakan PostgreSQL gratis/murah untuk MVP
Kenapa Drizzle, bukan Prisma?
| Kriteria | Prisma | Drizzle ✅ |
|---|---|---|
| Bundle size | ~10MB+ | ~100KB |
| Query style | Custom DSL | SQL-like |
| Type safety | ✅ | ✅ |
| Performance | Overhead (engine) | Zero overhead |
| Migration | Generate dari schema | Generate dari schema |
| Raw SQL | Sulit | Mudah |
| Learning curve | Sedang | Rendah (kalau tahu SQL) |
4. Cache & Session
| Teknologi | Versi | Peran |
|---|---|---|
| Redis | 7.x | In-memory data store untuk caching, session management, rate limiting, dan pub/sub. |
| Upstash Redis | Managed | Redis serverless — bayar per request, cocok untuk MVP/startup. |
Apa yang disimpan di Redis:
- Session tokens — Login session user (TTL 7 hari)
- Rate limit counters — Pencegahan abuse API (per IP / per user)
- AI context cache — Konteks percakapan terakhir user (TTL 30 menit)
- Leaderboard / streak data — Sorted sets untuk ranking harian
- Real-time state — Status mic, status companion, assessment progress
5. Authentication
| Teknologi | Versi | Peran |
|---|---|---|
| Better Auth | 1.x | Library autentikasi TypeScript-native, self-hosted, framework-agnostic. |
Kenapa Better Auth, bukan Clerk/Auth0/NextAuth?
- Self-hosted — Data user tetap di database kita sendiri, tidak tergantung vendor
- TypeScript-first — Type-safe API, auto-complete yang bagus
- Framework-agnostic — Bisa dipasang di Hono, Express, atau apapun
- Built-in features: Email/password, OAuth (Google, Apple), magic link, session management, 2FA
- Database adapter — Langsung pakai Drizzle adapter, data tersimpan di PostgreSQL kita
- Gratis — Open source, tidak ada biaya per-user
Auth Methods yang akan digunakan:
- Email + Password — Registrasi standar
- Google OAuth — Login cepat via Google
- Apple Sign-In — Untuk user iOS (wajib di App Store)
- Magic Link — Password-less via email (opsional)
6. AI Services
| Teknologi | Versi | Peran |
|---|---|---|
| Vercel AI SDK | 4.x | Unified interface untuk memanggil berbagai AI provider (OpenAI, Google AI, Anthropic). Mendukung streaming, tool calling, dan structured output. |
| OpenAI GPT-4o-mini | Latest | LLM utama untuk AI Companion — percakapan, evaluasi, feedback. Cost-effective dengan kualitas tinggi. |
| Google Gemini 2.0 Flash | Latest | LLM alternatif / fallback. Digunakan untuk assessment evaluation dan content generation. |
| Deepgram | Nova-2 | Speech-to-Text (STT) — Akurasi tinggi, latensi rendah, harga terjangkau. |
| OpenAI TTS | tts-1 / tts-1-hd | Text-to-Speech — Suara natural untuk AI Companion berbicara. |
Kenapa Vercel AI SDK?
- Provider-agnostic — Ganti dari OpenAI ke Gemini tanpa ubah kode
- Streaming built-in —
streamText()langsung jadi SSE - Structured output — Parse JSON dari LLM dengan Zod schema validation
- Tool calling — AI bisa memanggil fungsi (cek progress user, ambil konten)
- Telemetry — Log usage, latency, token count otomatis
7. Background Jobs & Queue
| Teknologi | Versi | Peran |
|---|---|---|
| BullMQ | 5.x | Job queue berbasis Redis untuk background processing. |
Job yang diproses di background:
- 🎤 Audio Processing — Konversi format audio user sebelum dikirim ke STT
- 🤖 AI Evaluation — Analisis pronunciation, grammar, fluency (bisa lambat, jangan blocking)
- 📊 Progress Calculation — Update learner profile, recalculate CEFR level
- 📧 Email Notifications — Kirim email verifikasi, reminder belajar
- 🧹 Cleanup Jobs — Hapus temporary audio files, expired sessions
8. File Storage
| Teknologi | Versi | Peran |
|---|---|---|
| Cloudflare R2 | S3-compatible | Object storage untuk file audio, gambar konten, dan media assets. |
Kenapa Cloudflare R2, bukan AWS S3?
- Zero egress fee — Tidak bayar saat user download/stream audio (S3 mahal untuk ini)
- S3-compatible API — Library yang sama, tinggal ganti endpoint
- Global CDN — File otomatis ter-cache di edge Cloudflare
- Murah — 10GB gratis, setelahnya ~$0.015/GB/bulan
File yang disimpan:
- Audio recording user (PCM → MP3/OGG)
- Audio TTS dari AI Companion
- Gambar untuk Picture Description mode
- Profile picture user
- Content media (gambar, icon topik)
9. Validation & Schema
| Teknologi | Versi | Peran |
|---|---|---|
| Zod | 3.x | Runtime validation & TypeScript schema definition. Dipakai di request body validation, environment variables, AI structured output, dan database schema. |
Contoh penggunaan:
// Request validation
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
nativeLanguage: z.enum(['id', 'en']),
});
// AI structured output
const EvaluationSchema = z.object({
overallScore: z.number().min(0).max(100),
pronunciation: z.number().min(0).max(100),
grammar: z.number().min(0).max(100),
fluency: z.number().min(0).max(100),
feedback: z.string(),
corrections: z.array(z.object({
original: z.string(),
corrected: z.string(),
explanation: z.string(),
})),
});
10. Monitoring & Logging
| Teknologi | Versi | Peran |
|---|---|---|
| Pino | 9.x | Structured JSON logger. Super cepat, low overhead. |
| Better Stack (Logtail) | Managed | Log aggregation & search. Free tier cukup untuk MVP. |
| Sentry | Latest | Error tracking & performance monitoring. |
11. Testing
| Teknologi | Versi | Peran |
|---|---|---|
| Vitest | 2.x | Unit & integration testing. Sama dengan frontend, konfigurasi shared. |
| Supertest | Latest | HTTP endpoint testing. Simulasi request ke Hono routes. |
| Testcontainers | Latest | Spin up PostgreSQL & Redis di Docker untuk integration test. |
12. Development Tools
| Teknologi | Peran |
|---|---|
| pnpm | Package manager (monorepo-friendly, hemat disk) |
| Turborepo | Monorepo build orchestration (sudah dipakai di project ini) |
| tsx | TypeScript runner untuk development (watch mode) |
| dotenv | Environment variable management |
| Biome | Linter + Formatter (lebih cepat dari ESLint + Prettier) |
Ringkasan Dependency
{
"dependencies": {
"hono": "^4.x",
"@hono/node-server": "^1.x",
"drizzle-orm": "^0.35.x",
"postgres": "^3.x",
"better-auth": "^1.x",
"ai": "^4.x",
"@ai-sdk/openai": "^1.x",
"@ai-sdk/google": "^1.x",
"bullmq": "^5.x",
"ioredis": "^5.x",
"@aws-sdk/client-s3": "^3.x",
"zod": "^3.x",
"pino": "^9.x",
"nanoid": "^5.x",
"dayjs": "^1.x"
},
"devDependencies": {
"typescript": "^5.x",
"tsx": "^4.x",
"vitest": "^2.x",
"drizzle-kit": "^0.25.x",
"@biomejs/biome": "^1.x",
"dotenv": "^16.x"
}
}
Environment Variables
# === Server ===
NODE_ENV=development
PORT=3001
API_BASE_URL=http://localhost:3001
# === Database ===
DATABASE_URL=postgresql://user:pass@localhost:5432/eljoy
# === Redis ===
REDIS_URL=redis://localhost:6379
# === Auth ===
BETTER_AUTH_SECRET=your-random-secret-min-32-chars
BETTER_AUTH_URL=http://localhost:3001
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
APPLE_CLIENT_ID=xxx
APPLE_CLIENT_SECRET=xxx
# === AI Services ===
OPENAI_API_KEY=sk-xxx
GOOGLE_GENERATIVE_AI_API_KEY=xxx
DEEPGRAM_API_KEY=xxx
# === Storage ===
R2_ACCOUNT_ID=xxx
R2_ACCESS_KEY_ID=xxx
R2_SECRET_ACCESS_KEY=xxx
R2_BUCKET_NAME=eljoy-media
# === Monitoring ===
SENTRY_DSN=https://xxx@sentry.io/xxx
LOGTAIL_SOURCE_TOKEN=xxx