Development
Architecture
System architecture and design patterns
Overview
Respondeo follows modern Next.js architecture best practices with a focus on server components, server actions, and type safety.
Technology Stack
Core Framework
- Next.js 16 — App Router with React Server Components
- React 19 — Server and client components
- TypeScript — Full type safety
- Bun — Fast JavaScript runtime and package manager
Database & ORM
- PostgreSQL — Primary relational database
- Drizzle ORM — Type-safe database queries
bun:sql— Bun's native PostgreSQL driver (zero dependencies)
Authentication
- BetterAuth — Modern authentication library
- OIDC Plugin — Generic OAuth provider support
- API Key Plugin — API authentication with rate limiting
Caching (Optional)
- Redis/Valkey — Optional caching layer
- Bun native client — Zero-dependency Redis client
- Cache-aside pattern — Manual cache invalidation
UI & Styling
- shadcn/ui — Component library (Base UI - Nova variant)
- Tailwind CSS 4 — Utility-first styling with CSS variables
- Lucide Icons — Icon library
- next-themes — Dark mode support
AI & Images
- AI SDK — Multi-provider AI integration (OpenAI, Anthropic, Google)
- Unsplash API — Image search and selection
Project Structure
quiz-app/
├── apps/
│ ├── web/ # Main application
│ │ ├── app/ # Next.js App Router
│ │ │ ├── (auth)/ # Auth pages
│ │ │ ├── (dashboard)/ # Main app pages
│ │ │ ├── actions/ # Server actions
│ │ │ └── api/ # API routes
│ │ ├── components/ # React components
│ │ ├── lib/ # Core libraries
│ │ │ ├── auth/ # Authentication
│ │ │ ├── db/ # Database & queries
│ │ │ ├── rbac/ # Permissions
│ │ │ ├── ai/ # AI integration
│ │ │ ├── cache/ # Caching layer
│ │ │ └── validations/ # Zod schemas
│ │ └── public/ # Static assets
│ └── docs/ # Documentation site
├── docs/ # Shared documentation
└── package.json # Workspace rootDesign Patterns
Server-First Architecture
The app prioritizes server components and server actions for:
- Performance — Minimal JavaScript sent to client
- Security — Sensitive logic stays on server
- SEO — Full server-side rendering
- DX — Collocated data fetching
Database Query Organization
Queries are organized by domain in lib/db/queries/:
quiz.ts— Quiz CRUD operationsattempt.ts— Attempt submission and leaderboardsuser.ts— User management
Each file exports functions like:
export async function getQuizById(id: string) {
// Implementation
}
export async function getQuizzes(options: PaginationOptions) {
// Implementation
}Validation with Zod
All input is validated using Zod schemas in lib/validations/:
import { z } from "zod";
export const createQuizSchema = z.object({
title: z.string().min(3).max(200),
description: z.string().max(1000).optional(),
questions: z.array(questionSchema).min(1).max(50),
});Schemas are reused across:
- Server actions
- API routes
- Form validation
RBAC System
Role-Based Access Control is implemented in lib/rbac/:
- Stateless — Roles resolved from OIDC groups at request time
- Environment-driven — All configuration via env vars
- Permission helpers —
hasPermission(),canEditQuiz(), etc. - API key inheritance — Keys inherit user's current permissions
See RBAC Guide for details.
Caching Strategy
Optional two-layer caching:
- Redis — Cross-request caching (5-10 minute TTL)
- React
cache()— Per-request deduplication
Cache keys follow the pattern:
quizzes:list:{admin|public}:{page}:{limit}
quizzes:detail:{id}
leaderboard:quiz:{id}:{page}:{limit}
leaderboard:global:{page}:{limit}See Caching Guide for details.
Error Handling
- Database errors — Caught and displayed with user-friendly messages
- Validation errors — Returned with specific field errors
- API errors — Consistent JSON error responses
- Rate limiting — 429 responses with retry info
Data Flow
Quiz Creation
- User submits form →
quiz-form.tsx - Client validates → Zod schema
- Server action →
app/actions/quiz.ts - Permission check →
canCreateQuiz(user) - Database insert →
createQuiz() - Cache invalidation → Clear quiz list cache
- Redirect → Quiz detail page
Quiz Attempt Submission
- User completes quiz →
quiz-player.tsx - Client submits answers →
app/actions/attempt.ts - Validate attempt → Check max attempts, time limit
- Fetch quiz data →
getCachedQuizById()(uses cache if available) - Grade answers → Compare with correct answers
- Save attempt →
saveQuizAttempt() - Leaderboard cache expires → TTL-based (no eager invalidation)
- Return results → Score, correct answers, leaderboard rank
API Request
- Client sends request with API key
- API key middleware validates key
- Permission check → Resolve user role, check permissions
- Rate limit check → 100 req/min per key
- Handle request → Call appropriate query function
- Return JSON response
Database Schema
Core Tables
- user — OIDC-synced user accounts
- session — Active sessions
- account — OAuth account links
- apikey — API keys for programmatic access
Quiz Tables
- quiz — Quiz definitions
- question — Questions (cascade delete on quiz delete)
- answer — Answer options (cascade delete on question delete)
Attempt Tables
- quiz_attempt — Completed attempts
- attempt_answer — Individual answers within attempts
All foreign keys use cascade deletes for clean data management.
Performance Optimizations
- Caching — Optional Redis for frequently accessed data
- Server Components — Minimize client JavaScript
- Pagination — All lists use cursor/offset pagination
- Database Indexes — On frequently queried columns
- Connection Pooling — Via PgBouncer for serverless (optional)
- Image Optimization — Next.js automatic image optimization
Security Measures
- OIDC Authentication — Delegated to identity provider
- Session Encryption — BetterAuth with secure secrets
- Permission Checks — On every server action and API route
- Input Validation — Zod schemas on all inputs
- Rate Limiting — API keys and guest plays
- SQL Injection Prevention — Drizzle ORM prepared statements
- CSRF Protection — Built into Next.js server actions
Testing
See Testing Guide for test strategy and running tests.
Next Steps
- Scripts Reference — Available commands
- Testing — Test suite overview
- RBAC Guide — Permission system
- Database Guide — Schema and queries