mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-03 18:23:47 +10:00
Rewrite every guide for the redesigned app, add guides for new features (documents, editor modes, check, cover letters, assistant, applications, self-hosting upgrade and environment reference), remove v5-only pages with redirects, and replace every screenshot.
148 lines
12 KiB
Plaintext
148 lines
12 KiB
Plaintext
---
|
|
title: "Project architecture"
|
|
description: "How the Reactive Resume monorepo fits together: the web and server apps, shared packages, runtime boundaries, and where new code belongs."
|
|
---
|
|
|
|
Reactive Resume is a TypeScript monorepo managed with pnpm workspaces and Turborepo. This page explains how the pieces fit together, so you can find the code behind a feature and know where a change belongs. To get a working checkout first, see [Development setup](/contributing/development).
|
|
|
|
## The big picture
|
|
|
|
There are two apps and a set of shared packages:
|
|
|
|
- **`apps/web`** is a client-rendered React 19 single-page app built with Vite, TanStack Router, TanStack Query, Tailwind CSS, and Lingui for translations.
|
|
- **`apps/server`** is a Hono application on Node.js. It serves the API, authentication, the MCP server, uploads, OpenAPI, and the built web app.
|
|
- **`packages/*`** hold everything the apps share: API business logic, authentication, database access, schemas, PDF and DOCX rendering, and UI primitives.
|
|
|
|
The browser talks to the server through [oRPC](https://orpc.unnoq.com/) at `/api/rpc`. [Better Auth](https://www.better-auth.com/) handles sign-in, sessions, passkeys, two-factor authentication, API keys, and the OAuth provider used by MCP clients. [Drizzle](https://orm.drizzle.team/) talks to PostgreSQL.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Browser["Browser: apps/web SPA"] -->|"oRPC /api/rpc"| Server
|
|
Browser -->|"Forme (WebAssembly)"| BrowserPDF["PDF in the browser"]
|
|
MCPClient["MCP client"] -->|"/mcp"| Server
|
|
|
|
subgraph Server["apps/server (Hono)"]
|
|
RPC["RPC and OpenAPI handlers"]
|
|
AuthRoutes["/api/auth"]
|
|
MCP["packages/mcp"]
|
|
end
|
|
|
|
RPC --> API["packages/api feature routers"]
|
|
MCP --> API
|
|
AuthRoutes --> Auth["packages/auth (Better Auth)"]
|
|
API --> DB["packages/db (Drizzle)"] --> Postgres[("PostgreSQL")]
|
|
API --> Storage[("Local disk, S3, or Vercel Blob")]
|
|
API --> Redis[("Redis (optional)")]
|
|
API --> ServerPDF["@reactive-resume/pdf/server"]
|
|
```
|
|
|
|
### How it runs
|
|
|
|
- **Development.** `pnpm dev` starts Vite on `PORT` (default `3000`), the Hono server on `SERVER_PORT` (default `3001`), and the email template preview on port `3002`. Vite proxies `/api`, `/mcp`, `/uploads`, `/.well-known`, and `/schema.json` to Hono, so you always open `http://localhost:3000`.
|
|
- **Docker.** The production image runs one Node.js process on port `3000`. Hono mounts the API, auth, MCP, and static routes, then serves the built web app.
|
|
- **Vercel.** One project deploys two services: `frontend` serves the static web build from the CDN, and `backend` runs the same Hono app in a Node.js Function. See [Deployment checks](/contributing/deployment-checks).
|
|
|
|
There is no request-time React server rendering. The web build prerenders the marketing homepage for each locale, and `apps/server/src/static/web.ts` serves HTML shells with OpenGraph, canonical, and JSON-LD metadata injected.
|
|
|
|
### What happens at startup
|
|
|
|
The server checks the environment, applies database migrations, and verifies the migrated schema before it initializes auth and accepts traffic. With `STRICT_SCHEMA_CHECK=true`, schema drift stops the server; otherwise it logs the drift and continues.
|
|
|
|
## Workspace map
|
|
|
|
| Workspace | What it owns |
|
|
| --- | --- |
|
|
| `apps/web` | Routes (`src/routes`, file-based), user-facing features (`src/features`), the PDF.js preview and public viewer, the PWA, and the oRPC browser client |
|
|
| `apps/server` | Hono route composition (`src/http`), RPC and OpenAPI adapters, the MCP transport, static and upload handlers, SEO for HTML shells, and startup checks |
|
|
| `packages/api` | oRPC procedures and business logic, one folder per feature under `src/features/*` |
|
|
| `packages/auth` | Better Auth configuration, helpers, and types |
|
|
| `packages/db` | Drizzle client and schema; generated migrations live in the root `migrations/` folder |
|
|
| `packages/env` | Server environment validation; loads the root `.env` |
|
|
| `packages/schema` | Zod schemas for resumes, cover letters, applications, pages, and templates |
|
|
| `packages/resume` | Pure resume logic with no database, HTTP, or DOM dependencies, such as JSON Patch helpers and social network icons |
|
|
| `packages/pdf` | Resume templates (which also lay out cover letters), the Forme adapter, font resolution, custom-style support, and browser and server PDF adapters |
|
|
| `packages/docx` | DOCX export |
|
|
| `packages/mcp` | MCP tools, prompts, resources, and the server card |
|
|
| `packages/ai` | AI provider types, prompts, and model-facing helpers |
|
|
| `packages/import` | Resume importers |
|
|
| `packages/ui` | Shared UI primitives and hooks in the Base UI / shadcn style |
|
|
| `packages/fonts` | Font metadata |
|
|
| `packages/email` | Email transport and templates |
|
|
| `packages/utils` | Narrow cross-cutting helpers behind explicit export subpaths |
|
|
| `packages/config` | Shared TypeScript and tooling configuration |
|
|
| `packages/dsh-plugin` | A separately built and published plugin that connects a DeepSeek Harness session to Reactive Resume over MCP |
|
|
| `tooling` | Development-only scripts: PDF translation catalog, semantic CSS reference, icon builds, database reset, deployment smoke test |
|
|
|
|
Internal packages are consumed as source through the `exports` map in each `package.json`, which points at `src` files. Don't expect a `dist` folder unless a package builds one explicitly.
|
|
|
|
## Where new code goes
|
|
|
|
| You are changing | Put it here |
|
|
| --- | --- |
|
|
| A page, loader, or user workflow | A route in `apps/web/src/routes` plus the feature folder in `apps/web/src/features/<area>` |
|
|
| An authenticated API procedure or business rule | `packages/api/src/features/<area>` |
|
|
| Pure resume data behavior | `packages/resume` |
|
|
| The shape of resume data | `packages/schema` first, then API DTOs, importers, PDF templates, and web forms that use it |
|
|
| A PDF template or rendering behavior | `packages/pdf` |
|
|
| PDF.js canvas or viewer UI | `apps/web/src/features/resume` (never `packages/pdf`) |
|
|
| DOCX export | `packages/docx` |
|
|
| An MCP tool, prompt, or resource | `packages/mcp` |
|
|
| A generic UI primitive or hook | `packages/ui`; workflow-specific UI stays in its web feature |
|
|
| A database column or table | `packages/db/src/schema/*`, then `pnpm db:generate` |
|
|
| A server environment variable | `packages/env/src/server.ts`, `.env.example`, and `globalEnv` in `turbo.json` |
|
|
| A dev-only script | `tooling/` |
|
|
|
|
A new template touches several places: `packages/schema/src/templates.ts`, `packages/pdf/src/templates/index.ts`, the template source under `packages/pdf/src/templates/<name>/`, and preview images under `apps/web/public/templates/{jpg,pdf}`.
|
|
|
|
Add a helper to `packages/utils` only when no domain package is a better owner. JSON Patch behavior belongs in `@reactive-resume/resume/patch`, and DOCX builders belong in `@reactive-resume/docx`.
|
|
|
|
## Boundary rules
|
|
|
|
Turborepo enforces these rules with `pnpm exec turbo boundaries`:
|
|
|
|
- Import other workspaces by package name and export subpath, such as `@reactive-resume/pdf/browser`. Never reach into another workspace's `src` through a relative path, `@reactive-resume/*/src/*`, or a TypeScript path alias.
|
|
- Each workspace's `turbo.json` declares tags. `app:web` and `app:server` mark the apps. `runtime:server` marks server-only packages (API, auth, database, environment, email, MCP), `runtime:browser` marks browser-only UI, and `runtime:universal` marks environment-neutral domain packages.
|
|
- Runtime-specific code sits behind explicit subpaths such as `@reactive-resume/pdf/browser`, `@reactive-resume/pdf/server`, and `@reactive-resume/env/server`. Keep root exports environment-neutral unless the whole package is server-only.
|
|
- Wildcard exports are reserved for leaf libraries with a file-like surface: `@reactive-resume/ui/components/*`, `@reactive-resume/ui/hooks/*`, and the schema model files. Everything else uses explicit exports.
|
|
|
|
After you change a shared contract, an export, or an import path, run `pnpm exec turbo boundaries` and check the affected consumers.
|
|
|
|
## The web app
|
|
|
|
`apps/web/src/routes` stays route-owned: route files handle the URL, loaders, redirects, and metadata. Implementation lives in `apps/web/src/features`, grouped by product area: `documents`, `resume` (editor, preview, export, sharing, custom styles), `letters`, `applications`, `assistant`, `ats-checker`, `settings`, `command-palette`, `auth`, `homepage`, `theme`, `locale`, and `user`.
|
|
|
|
`apps/web/src/router.tsx` creates the router context with `queryClient`, `orpc`, `theme`, `locale`, `session`, and `flags`. Read these from route context instead of fetching them again. Never edit `routeTree.gen.ts` by hand; Vite regenerates it when you add or rename a route.
|
|
|
|
The oRPC client in `apps/web/src/libs/orpc/client.ts` calls `/api/rpc` with credentials. On Vercel, `apps/web/src/libs/orpc/fetch.ts` stages large request bodies through Blob storage.
|
|
|
|
When you add a public marketing route, also update its server fallback and SEO handling in `apps/server/src/static/web.ts`. Vite's dev fallback can hide a production 404.
|
|
|
|
## The API
|
|
|
|
`packages/api/src/routers/index.ts` combines the feature routers (`resume`, `coverLetters`, `documents`, `applications`, `agent`, `ai`, `aiProviders`, `auth`, `storage`, `statistics`, `flags`) into the contract served at `/api/rpc`. Each feature folder owns its procedures, services, helpers, and tests.
|
|
|
|
Use `protectedProcedure` from `packages/api/src/context.ts` for authenticated procedures, and check resource ownership inside the feature logic. API keys, bearer tokens, and cookies all resolve through the same shared auth path; don't add a separate one.
|
|
|
|
Keep helpers inside the feature that uses them. Don't reintroduce technical-layer folders such as `services/` or `helpers/` at the package root.
|
|
|
|
## PDF rendering
|
|
|
|
`packages/pdf` renders every PDF. Templates are React components built from the package's primitives. The code in `src/forme` renders them with a small React reconciler and converts the result into a [Forme](https://www.formepdf.com/) document. The Forme engine, compiled to WebAssembly, lays out and draws the pages. No Chromium, Browserless, or print service is involved.
|
|
|
|
- `@reactive-resume/pdf/browser` creates PDFs in the browser. The editor's download and preview use it.
|
|
- `@reactive-resume/pdf/server` creates PDFs on the server, for the public resume download and API exports.
|
|
- `packages/pdf/src/templates/shared/filtering.ts` holds the section filtering shared by all templates. Template-specific visual exceptions stay in that template's folder.
|
|
- `packages/pdf/src/hooks/use-register-fonts.ts` resolves font families, weights, and fallback stacks for other scripts.
|
|
|
|
Default section titles in the PDF come from a generated catalog, `packages/pdf/src/section-title-catalog.json`, built from the web app's translations. See [Contributing translations](/contributing/translations#updating-catalogs-in-a-checkout).
|
|
|
|
## MCP
|
|
|
|
`packages/mcp` implements the MCP server with canonical, unprefixed tool names such as `list_resumes`, `read_resume`, `apply_resume_patch`, `list_cover_letters`, and `list_applications`. The server process imports it from `@reactive-resume/mcp` and injects an in-process oRPC router client, so MCP tools run the same business logic as the web app. MCP must never import code from `apps/web`. For the user-facing side, see [Using the MCP server](/guides/using-the-mcp-server).
|
|
|
|
## Related pages
|
|
|
|
- [Development setup](/contributing/development): run the app locally and learn the everyday commands.
|
|
- [Deployment checks](/contributing/deployment-checks): how CI verifies the Vercel build and how to smoke-test an installation.
|
|
- [Contributing translations](/contributing/translations): Crowdin, the glossary, and catalog commands.
|