Skip to content

Server & API

The backend is an Express.js application in apps/api/, backed by PostgreSQL via Drizzle ORM and Better Auth for session management. Shared logic (schema, auth config, environment parsing, types) lives in packages/ and is consumed by the API server.

apps/api/
└── src/
├── index.ts # Express app + route registration
├── handlers/ # One file per domain (calendars, events, invites…)
└── middleware/ # Auth guard, error handler, request logger
packages/
├── auth/ # Better Auth server configuration
├── db/ # Drizzle ORM schema + query helpers
├── config/ # Environment variable loading and validation
└── types/ # Shared TypeScript types (used by client too)

All routes are prefixed with /api. Routes marked 🔒 require a valid bearer token (the Authorization: Bearer <token> header, set automatically by the client).

Better Auth handles everything under /api/auth/* — sign-in, sign-up, session, password reset. These routes are managed by the toNodeHandler(auth) adapter; you don’t write them manually.

MethodPathAuthDescription
GET/api/serverReturns version and minClientVersion
GET/api/server/okHealth check — returns 200 OK
MethodPathAuthDescription
GET/api/calendars🔒Get all calendars the user is a member of
GET/api/calendars/:id🔒Get a single calendar by ID
GET/api/calendars/tokens/:token🔒Get calendar data from an invite token
POST/api/calendars🔒Create a new calendar
POST/api/calendars/members/:calendarId🔒Join a calendar (accept invite)
POST/api/calendars/invites🔒Create a shareable invite link
PUT/api/calendars🔒Update a calendar
DELETE/api/calendars🔒Delete a calendar
DELETE/api/calendars/members/:calendarId🔒Leave a calendar
MethodPathAuthDescription
GET/api/events🔒Get all events across the user’s calendars
POST/api/events🔒Create an event
PUT/api/events🔒Update an event
DELETE/api/events🔒Delete an event
MethodPathAuthDescription
GET/api/stream🔒SSE stream — server pushes event updates

The client holds this connection open for the duration of the session. When another user in a shared calendar creates, updates, or deletes an event, the server broadcasts the change to all connected clients in that calendar.

MethodPathAuthDescription
GET/api/users/settings🔒Get the current user’s settings
PUT/api/users/settings🔒Save settings
MethodPathAuthDescription
DELETE/api/users🔒Delete the current user’s account

Configures Better Auth and exports the auth instance used by the API. Handles:

  • Email / password sign-in and sign-up
  • Session management via bearer tokens
  • Password reset emails (requires SMTP config)
  • Trusted origins for the Expo client (both dev and production URLs)

Entry point: packages/auth/src/index.ts

Database access via Drizzle ORM. Exports the db client and typed query helpers for each domain.

PathPurpose
src/schema.tsTable definitions
src/queries/calendars.tsCalendar CRUD + member management
src/queries/events.tsEvent CRUD
src/queries/invites.tsInvite creation
src/queries/settings.tsUser settings
src/queries/users.tsUser lookup and deletion
drizzle/Migration SQL files

Loads and validates environment variables at startup. Exports a typed config object used by auth, db, and the API server.

TypeScript types shared between client and server. The canonical source of truth for the data model — Calendar, Event, Invite, User, Settings.


VariableRequiredDescription
DATABASE_URLPostgreSQL connection string
BETTER_AUTH_SECRETSecret for signing sessions. Keep this private and stable
BETTER_AUTH_URLOrigin of the API server — no trailing slash, no /api suffix
API_SERVER_PORTPort to listen on. Defaults to 3000
ENVIRONMENTSet to dev to enable localhost CORS. Anything else is treated as production
SMTP_HOSTSMTP server for transactional email
SMTP_PORTSMTP port (typically 587)
SMTP_USERSMTP username
SMTP_PASSSMTP password
FROM_EMAILSender address for password reset emails

Terminal window
# Generate a new migration after editing the schema
pnpm --filter @musubi/db generate
# Apply pending migrations
pnpm --filter @musubi/db migrate
# or from the monorepo root:
pnpm db:migrate