Implements the first temetro backend: an Express 5 API on Postgres via Drizzle ORM, with authentication and multi-tenant clinics powered by Better Auth. - Auth: email/password with required email verification, password reset, rate limiting, CSRF/trusted-origins, secure cookies, session audit hook. - Organizations (clinics) with RBAC (owner/admin/member/viewer) and an extended `patient` permission set; member invitations by email. - Org-scoped patient records mirroring the frontend Patient shape, with CRUD endpoints gated by permission (read/write/delete). - Email helper logs links to the console when SMTP is unset (zero-setup local dev); Dockerfile + docker-compose (db + backend + frontend) with migrations applied on startup and a configurable Postgres host port. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.8 KiB
temetro backend
TypeScript + Express + Postgres API for temetro. Authentication is powered by
Better Auth (email/password with verification, password reset, and
multi-tenant organizations with role-based access control). Patient records are scoped to an
organization (a clinic) and exposed over a small REST API that mirrors the frontend's Patient
shape exactly.
Quick start (Docker)
cp .env.example .env
# set a strong secret:
# openssl rand -base64 32 -> paste into BETTER_AUTH_SECRET
docker compose up # db + backend + frontend
- Frontend → http://localhost:3000
- Backend → http://localhost:4000 (health:
GET /health, auth health:GET /api/auth/ok) - Postgres → localhost:5432
Run only the API + database: docker compose up db backend.
Browse the DB with Adminer: docker compose --profile tools up adminer → http://localhost:8080.
Migrations are applied automatically on container start (node dist/migrate.js).
Local development (without Docker)
npm install
cp .env.example .env # point DATABASE_URL at a local Postgres (localhost:5432)
npm run db:migrate # apply migrations (drizzle-kit)
npm run dev # tsx watch on http://localhost:4000
Auth & schema workflow
The Better Auth tables are generated into src/db/schema/auth.ts from src/auth.ts. Re-run after
changing auth config/plugins, then regenerate the SQL migration:
npm run auth:generate # better-auth CLI -> src/db/schema/auth.ts
npm run db:generate # drizzle-kit -> ./drizzle/*.sql
npm run db:migrate # apply
API
All patient routes require a signed-in user (Better Auth session cookie) and an active organization; access is gated by the caller's clinic role.
| Method | Path | Permission | Notes |
|---|---|---|---|
| GET | /api/patients |
patient:read |
list patients in the active clinic |
| GET | /api/patients/:fileNumber |
patient:read |
one patient (404 if absent) |
| POST | /api/patients |
patient:write |
create (409 if file number exists) |
| PUT | /api/patients/:fileNumber |
patient:write |
replace the full record |
| DELETE | /api/patients/:fileNumber |
patient:delete |
remove a patient |
Auth endpoints (sign up / in / out, verify email, reset password, organizations & invitations) are
served by Better Auth under /api/auth/*.
Roles
| Role | Patient access | Clinic management |
|---|---|---|
owner |
read / write / delete | full |
admin |
read / write / delete | members, invitations, settings |
member (clinician) |
read / write | — |
viewer |
read | — |
Environment
See .env.example. If SMTP_HOST is unset, verification / reset / invitation emails are printed
to the server console instead of being sent — no setup required for local development.