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.
Structure
Section titled “Structure”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)API endpoints
Section titled “API endpoints”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.
Server
Section titled “Server”| Method | Path | Auth | Description |
|---|---|---|---|
GET | /api/server | — | Returns version and minClientVersion |
GET | /api/server/ok | — | Health check — returns 200 OK |
Calendars
Section titled “Calendars”| Method | Path | Auth | Description |
|---|---|---|---|
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 |
Events
Section titled “Events”| Method | Path | Auth | Description |
|---|---|---|---|
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 |
Real-time stream
Section titled “Real-time stream”| Method | Path | Auth | Description |
|---|---|---|---|
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.
Settings
Section titled “Settings”| Method | Path | Auth | Description |
|---|---|---|---|
GET | /api/users/settings | 🔒 | Get the current user’s settings |
PUT | /api/users/settings | 🔒 | Save settings |
| Method | Path | Auth | Description |
|---|---|---|---|
DELETE | /api/users | 🔒 | Delete the current user’s account |
Shared packages
Section titled “Shared packages”packages/auth
Section titled “packages/auth”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
packages/db
Section titled “packages/db”Database access via Drizzle ORM. Exports the db client and typed query helpers for each domain.
| Path | Purpose |
|---|---|
src/schema.ts | Table definitions |
src/queries/calendars.ts | Calendar CRUD + member management |
src/queries/events.ts | Event CRUD |
src/queries/invites.ts | Invite creation |
src/queries/settings.ts | User settings |
src/queries/users.ts | User lookup and deletion |
drizzle/ | Migration SQL files |
packages/config
Section titled “packages/config”Loads and validates environment variables at startup. Exports a typed config object used by auth, db, and the API server.
packages/types
Section titled “packages/types”TypeScript types shared between client and server. The canonical source of truth for the data model — Calendar, Event, Invite, User, Settings.
Environment variables
Section titled “Environment variables”| Variable | Required | Description |
|---|---|---|
DATABASE_URL | ✓ | PostgreSQL connection string |
BETTER_AUTH_SECRET | ✓ | Secret for signing sessions. Keep this private and stable |
BETTER_AUTH_URL | ✓ | Origin of the API server — no trailing slash, no /api suffix |
API_SERVER_PORT | — | Port to listen on. Defaults to 3000 |
ENVIRONMENT | — | Set to dev to enable localhost CORS. Anything else is treated as production |
SMTP_HOST | — | SMTP server for transactional email |
SMTP_PORT | — | SMTP port (typically 587) |
SMTP_USER | — | SMTP username |
SMTP_PASS | — | SMTP password |
FROM_EMAIL | — | Sender address for password reset emails |
Running migrations
Section titled “Running migrations”# Generate a new migration after editing the schemapnpm --filter @musubi/db generate
# Apply pending migrationspnpm --filter @musubi/db migrate# or from the monorepo root:pnpm db:migrate