Files
gh-christianlouis-inboxconv…/docs/adr/008-nextjs-frontend.md
T

146 lines
5.7 KiB
Markdown

# ADR 008: Use Next.js for Frontend
**Status:** Accepted
**Date:** 2026-02-10
**Deciders:** Development Team
## Context
The SaaS platform requires a web frontend that provides:
1. User authentication (email/password + Google OAuth)
2. Mail account management dashboard (CRUD operations)
3. Processing run history and statistics
4. Settings and subscription management
5. Responsive design for mobile and desktop
Technical requirements:
- Type-safe API integration with the FastAPI backend
- Server-side rendering (SSR) for SEO and initial load performance
- Static export capability for CDN hosting
- Easy Docker containerization
- Modern development tooling (TypeScript, ESLint, hot-reload)
## Decision
We will use **Next.js 14+** (App Router) with **TypeScript** and **Tailwind CSS** for the frontend.
## Alternatives Considered
### 1. React (Create React App / Vite SPA)
- **Pros**: Simpler build setup, no SSR complexity, large ecosystem
- **Cons**: Client-side rendering only (SEO limitations), no built-in routing, additional setup for SSR
### 2. Vue.js / Nuxt.js
- **Pros**: Excellent developer experience, reactive by default, SSR via Nuxt
- **Cons**: Smaller ecosystem than React, team less familiar, different component model
### 3. Svelte / SvelteKit
- **Pros**: Very small bundle size, simple reactive model, SSR via SvelteKit
- **Cons**: Smaller community, fewer UI component libraries, less mature ecosystem
### 4. Angular
- **Pros**: Full framework (routing, forms, HTTP), TypeScript-first
- **Cons**: Heavy boilerplate, steep learning curve, over-engineered for a dashboard application
### 5. Server-Side Templates (Jinja2 / FastAPI with Jinja)
- **Pros**: Simplest deployment (single backend), no separate frontend build
- **Cons**: No reactive UI, poor UX for dynamic dashboards, hard to test independently
## Rationale
Next.js was chosen because:
1. **App Router**: File-system based routing with layout nesting simplifies page organization
2. **TypeScript First**: Strong typing catches API integration errors at compile time
3. **API Route Proxy**: Next.js Route Handlers allow proxying backend requests at runtime — eliminating CORS issues and enabling `BACKEND_URL` to be set at container runtime (not build time)
4. **SSR + Static**: Supports both server-rendered pages (authenticated dashboard) and static pages (marketing landing page)
5. **Tailwind CSS**: Utility-first CSS eliminates the need for a separate CSS framework and enables rapid UI development
6. **Large Ecosystem**: Extensive component libraries, excellent documentation, widely used
7. **Docker Friendly**: `next start` serves the production build, easily containerized
## Implementation Architecture
### Frontend Proxy Pattern
The frontend proxies all API calls through a Next.js Route Handler to avoid CORS and build-time URL issues:
```
Browser → Next.js Server (/api/v1/*) → FastAPI Backend (BACKEND_URL/api/v1/*)
```
```typescript
// src/app/api/v1/[...path]/route.ts
const BACKEND_URL = process.env.BACKEND_URL ?? "http://localhost:8000";
export async function GET(req: NextRequest, { params }: { params: { path: string[] } }) {
const path = params.path.join("/");
const url = `${BACKEND_URL}/api/v1/${path}${req.nextUrl.search}`;
return fetch(url, { headers: req.headers });
}
```
This means `BACKEND_URL` is a **runtime** environment variable (set in Docker Compose / Kubernetes), not a build-time variable. The Axios client uses a relative base URL:
```typescript
// src/lib/api.ts
const api = axios.create({ baseURL: "/api/v1" });
```
### Project Structure
```
frontend/src/
├── app/ # Next.js App Router pages
│ ├── page.tsx # Landing page
│ ├── login/ # Authentication pages
│ ├── dashboard/ # Main dashboard
│ ├── accounts/ # Mail account management
│ ├── settings/ # User settings
│ └── api/v1/[...path]/ # Backend proxy Route Handler
├── components/ # Reusable UI components
│ ├── AddMailAccountModal.tsx
│ ├── DashboardLayout.tsx
│ └── AuthGuard.tsx
├── lib/
│ └── api.ts # Typed Axios API client
├── store/
│ └── authStore.ts # Zustand authentication state
└── instrumentation.ts # Server startup hook (logs BACKEND_URL)
```
### Authentication State
Authentication state is managed via **Zustand** store, with JWT tokens persisted in `localStorage`. The `AuthGuard` component protects all dashboard routes.
## Consequences
### Positive
- TypeScript catches API contract mismatches at compile time
- Proxy pattern eliminates CORS configuration and build-time URL embedding
- App Router layouts reduce boilerplate for authenticated vs public pages
- Tailwind CSS enables rapid UI iteration without writing custom CSS
- Server instrumentation hook logs `BACKEND_URL` at startup for easy debugging
### Negative
- Next.js adds complexity vs a plain React SPA (SSR concepts, server/client component boundary)
- Node.js 20+ required at runtime (not just build time)
- Separate Docker container needed (backend and frontend are distinct services)
- `localStorage` token storage is vulnerable to XSS (future: migrate to `httpOnly` cookies)
### Neutral
- ESLint with `eslint-config-next` enforces Next.js-specific rules
- `npm ci` used in CI to ensure deterministic installs from `package-lock.json`
## Related Decisions
- See ADR-007 for JWT tokens consumed by the frontend
- See ADR-003 for the FastAPI backend the frontend proxies to
## References
- [Next.js Documentation](https://nextjs.org/docs)
- [Next.js App Router](https://nextjs.org/docs/app)
- [Tailwind CSS](https://tailwindcss.com/)
- [Zustand State Management](https://github.com/pmndrs/zustand)