Tailr is a desktop-first resume/CV builder: a React/Vite frontend paired with a TypeScript Express backend (Prisma + Postgres). This repository highlights production-ready backend engineering: typed DB access, connection pooling, auth integration, layered services, and operational build strategies.
- Demonstrates practical backend engineering: TypeScript-driven safety, Prisma schema design, a custom DB adapter with
pg.Poolto manage connections, and a build strategy that keeps Prisma client resolution predictable across dev and prod. - Shows tradeoffs and pragmatic choices: JSON payloads for flexible CV content, generated Prisma client location for reliable compiled outputs, and middleware-based concerns (auth, rate-limiting, validation).
- Backend: Node.js, Express, TypeScript
- ORM: Prisma (Postgres) with
@prisma/adapter-pgandpgpooling - Auth: Clerk
- Frontend: React + Vite (context), Redux
- Notifications (client): react-toastify
- Monorepo:
client/(frontend) andserver/(backend). - Server layout (src):
src/server.ts,src/app.ts— app bootstrap and route mountingsrc/lib/prisma.ts— Prisma client factory handling dev vs compiled locations and poolingsrc/middleware/*— auth, rate limiting, and helperssrc/modules/*— domain folders (controllers → services → repositories)src/generated/prisma— generated Prisma client (compiled intodist/src/generated/prisma)
Request flow (example: update CV):
- Express route receives request and runs middleware (auth, rate limit).
- Controller validates input and calls appropriate service.
- Service orchestrates business logic and calls repository.
- Repository uses Prisma client to interact with Postgres.
- We generate the Prisma client into
server/src/generated/prismaduring development sotsccompiles it intodist/src/generated/prismafor production. src/lib/prisma.tsattempts to load the generated client from several locations (compiled and source) and falls back to@prisma/clientwhere appropriate. This pattern keeps runtime import paths robust acrossts-nodedev and compileddistproduction.- CI recommendation: run
npx prisma generatebeforenpm run build(or add it into thebuildscript) to ensure generated client is present.
Prereqs: Node 18+, PostgreSQL
Server:
cd server
npm install
# set server/.env with DATABASE_URL and Clerk keys
npx prisma generate
npx prisma migrate dev # or `npx prisma db push` for quick iteration
npm run devClient:
cd client
npm install
# set VITE_CLERK_PUBLISHABLE_KEY in client/.env
npm run devProduction build (server):
cd server
npx prisma generate
npm run build
npm startExample server/.env:
DATABASE_URL=postgresql://user:password@localhost:5432/tailr_dev
CLERK_SECRET=pk_test_XXXXXXXXXXXXXXXX
-
Important endpoints:
GET /api/cvs— list the authenticated user's CVsGET /api/cvs/:id— retrieve a single CV owned by the userPOST /api/cvs— create a new CV (owner = authenticated user)PATCH /api/cvs/:id— update CV content (owner-checked)DELETE /api/cvs/:id— delete CV (owner-checked)
-
Data model (Prisma):
User(id, firstName, lastName, email, clerkId, timestamps)Cv(id, userId, name, jobTitle, company, content: Json, timestamps)
Design note: storing content as JSON gives frontend flexibility for templates/sections while preserving relational owners and indexes.
- Auth: Clerk integration enforces identity; middleware extracts
clerkUserIdfor downstream checks. - Authorization: services/repositories confirm
userIdownership prior to mutations. - Rate limiting & global guards protect endpoints from abuse.
- Input validation occurs at controller/service boundaries; Prisma ensures parameterized queries to avoid injection.
- Logging: critical errors and lifecycle events are logged — swap in pino/winston for structured logs.
- Metrics: instrument controllers/services for request durations, DB latency; export Prometheus metrics in production.
- Scaling: keep app stateless; use connection pooling (
pg.Pool) and consider PgBouncer for large horizontal scale to avoid DB connection limits.
- Open issues for bugs or feature requests.
- Send PRs with tests and a clear description of changes.
This repository is provided as-is. Add a license file if you intend to publish.
