Architecture
Request flow
AuthUser. User-scoped services (profile, account) receive the
verified AuthUser and filter queries by user.id; shared discovery and
analytics services (discovery, velocity) return typed Pydantic models over
discovery.companies or app.signals_timeseries.
Modules
app/api/v1/— HTTP surface:account,profile,organizations,velocity(metrics),auth(dependency), andapi(router aggregation).app/core/— cross-cutting setup: config, logging, metrics, middleware, rate limiting.app/models/— SQLModel mappings for the Supabaseappschema (app.profiles).app/schemas/— Pydantic request/response models (auth,organizations,profile,velocity).app/services/— business logic, repositories, and clients: Postgres (db_engine,profile,profile_repo,account,discovery,discovery_repo,velocity).app/temporal/— boilerplate client and worker; add workflows here when needed.app/utils/auth.py— Supabase JWKS token verification.
Design decisions
- Users live in Supabase Auth. The API stores only
auth.users.idreferences. - Backend-only schema.
appis service-role only: RLS enabled, no policies. - Postgres as single operational and analytics store. Postgres handles the transactional graph (entities, affiliations, discrete
entity_signals) as well as append-only metric observations (app.signals_timeseries) used for rolling growth velocity. - Discovery view, not raw graph. The read endpoints query
discovery.companies(a denormalized view), never the normalized tables, so the frontend gets typed contracts and never has to join or handlemerged_into_id. - Keyset pagination. List endpoints paginate with an opaque
next_cursorover a stable sort key, notLIMIT/OFFSET, so deep pages stay index-friendly. - One async engine.
app/services/db_engine.pyowns the pooled connection used by all Postgres repositories and the health check. - Errors are 400/401/404/503. Missing or foreign rows are indistinguishable from absent rows, so ids cannot be probed; invalid query parameters return 400, and unavailable upstream dependencies (JWKS, Auth Admin API) return 502/503.