temetro

Architecture

How temetro is put together — frontend, backend, database, and real-time.

temetro is a monorepo with two independent apps that talk over HTTP and WebSockets:

proxy.ts
index.ts
auth.ts
docker-compose.yml

The pieces

LayerTechnologyNotes
FrontendNext.js 16 (App Router), React 19, Tailwind v4COSS / Base UI components; chat UI built on custom ai-elements
BackendNode.js ≥ 20, Express 5, TypeScriptREST API under /api/*, plus Socket.io for real-time
DatabasePostgreSQL 17, Drizzle ORMMigrations generated by drizzle-kit, applied automatically on startup
AuthBetter AuthEmail/password + username plugin; organization plugin = clinics; cookie sessions
Real-timeSocket.ioMessages and notifications pushed live; authenticated by the session cookie

Request flow

  1. The browser talks to the frontend (:3000), which renders the UI.
  2. The frontend's lib/api-client.ts calls the backend (:4000, configured by NEXT_PUBLIC_API_URL) with the session cookie attached; a 401 bounces the user to /login.
  3. Every /api/* route requires a signed-in user and an active organization (clinic). Handlers check the role-based permissions defined in src/lib/access.ts — the same matrix shown in Roles & permissions, enforced server-side.
  4. Mutations write an entry to the activity log and create notifications, which Socket.io pushes to connected clients.

Database tables

GroupTables
Patientspatients, plus position-ordered satellites: patient_allergies, patient_medications, patient_problems, patient_labs, patient_encounters
Clinicalappointments, prescriptions, tasks, notes
Messagingconversations, conversation_participants, messages
Systemactivity_log, notifications
Auth (Better Auth)user, session, account, organization, member, invitation

Every clinic-owned table carries an organizationId — multi-tenancy is enforced by scoping every query to the caller's active organization.

Multi-tenancy & auth in one paragraph

Better Auth's organization plugin models clinics. A user signs up, then either creates an organization (becoming owner) or joins one via invitation. The session tracks an active organization; the backend resolves it on every request and scopes all reads and writes to it. Roles (owner, admin, doctor, reception, pharmacy, lab, member) and their permission statements (patient, appointment, prescription, task, lab) live in backend/src/lib/access.ts, with a mirror in frontend/lib/access.ts so the UI can hide what the server would reject. The frontend derives its route gating from the same permissions: full clinicians are probed via prescription:delete, the pharmacy area via prescription:write, and the lab area via lab:write.

On this page