mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-05-29 11:18:46 -04:00
4.0 KiB
4.0 KiB
backend/api
Express 5 REST + WebSocket server. One file per endpoint under src/. Runs at
https://api.compassmeet.com in prod, http://localhost:8088 locally.
See README.md for full Google Cloud setup, deployment, SSH access, and DNS. Cross-package context lives in the root CLAUDE.md.
Layout
backend/api/
├── src/
│ ├── serve.ts Entry point (tsx watch in dev)
│ ├── app.ts Express setup + handlers map (~line 583)
│ ├── routes.ts Route definitions
│ ├── helpers/
│ │ ├── endpoint.ts APIHandler<>, APIError, withRateLimit, auth glue
│ │ └── private-messages.ts
│ ├── <verb>-<resource>.ts One file per endpoint (get-me.ts, create-event.ts, ...)
│ └── public/ Static assets copied into dist
├── tests/unit/ Jest unit tests
├── Dockerfile, main.tf Cloud Run / Terraform
└── deploy-api.sh, ssh-api.sh Manual ops
Endpoint flow
-
Schema: add the entry (method,
authed, Zodprops,returns) incommon/src/api/schema.ts. -
Handler: create
src/<verb>-<resource>.ts:import {APIHandler, APIError} from './helpers/endpoint' export const getThing: APIHandler<'get-thing'> = async (props, auth) => { // auth.uid available when authed: true return {...} } -
Register in the
handlersmap insrc/app.ts.
Conventions
- Database:
createSupabaseDirectClient()fromshared/supabase/init— pg-promise (oneOrNone,manyOrNone,none). Never string-concatenate SQL; use helpers fromshared/supabase/utils(insert,update,updateData,bulkUpsert, ...) orrenderSql/select/from/wherefromshared/supabase/sql-builder. SQL is lowercase by convention. - Auth: handler gets
(props, auth).auth.uidis the user ID,auth.creds.kindisfirebase|key|session. - Errors:
throw APIError(404, 'User not found')— never rawError. Standard codes: 400, 401, 403, 404, 429, 500. - Rate limiting: wrap with
withRateLimit(handler, {name, limit, windowMs})when an endpoint can be abused. - WebSocket broadcasts: import from
shared/websockets/helpers(broadcastUpdatedUser, ...). Topics defined there are whatuseApiSubscriptionon the frontend can listen to. - Logging:
log.info / log.errorfromshared/monitoring/log. Neverconsole.log. - Transactions over multi-step state: don't
sleep()waiting for eventual consistency. Build atomic inserts (seecreate-user-and-profile.tsfor the pattern). - Test files:
*.unit.test.tsnext to or undertests/unit/. Mock pg-promise with a fakeoneOrNone/manyOrNonerather than spinning up a DB. See../../docs/testing.md.
Build / run / test
yarn --cwd=backend/api dev # tsx watch on src/serve.ts
yarn --cwd=backend/api build # tsc → dist/
yarn --cwd=backend/api typecheck
yarn --cwd=backend/api lint[-fix]
yarn --cwd=backend/api test [path/to/file.unit.test.ts]
yarn --cwd=backend/api regen-types-dev # rebuild common/src/supabase/schema.ts from dev DB
yarn dev from the repo root brings up web + this API together (preferred for most work).
Deployment
Push to main triggers GitHub Actions deploy. Manual: ./deploy-api.sh prod (see README.md).
SSH for logs / debugging: ./ssh-api.sh prod. Secrets live in Google Cloud Secrets Manager — names in
common/src/secrets.ts.