@factory/api
REST API v1 for the AI Software Factory — a NestJS 10 modular monolith
serving POST/GET/PATCH/DELETE on http://localhost:4000/api/v1.
Stack: NestJS (plain, hand-written modules) · TypeScript strict · Prisma 5
(PostgreSQL, schema at ../../prisma/schema.prisma) · ioredis · Zod ·
hand-rolled HS256 JWT (src/lib/jwt.ts, node:crypto only). BullMQ is not
here — the worker app owns queues; this service publishes domain events to the
Redis stream factory:events via EventBus.
Run
pnpm install # from the repo root
pnpm --filter @factory/api dev # tsx watch src/main.ts (default :4000)
Env vars: copy .env.example to .env (dev-safe defaults are built in —
the app boots without it for local smoke tests).
Build / start / check:
pnpm --filter @factory/api build # tsc → dist/
pnpm --filter @factory/api start # node dist/main.js
pnpm --filter @factory/api typecheck
pnpm --filter @factory/api test # vitest (unit tests, no DB required)
Health
| Endpoint | Description |
|---|---|
GET /api/v1/health |
Liveness: {status:"ok", version, uptime}. Public. |
GET /api/v1/health/deep |
Readiness: Postgres SELECT 1 + Redis PING; 503 SERVICE_UNAVAILABLE when a dependency is down. Public. |
Module map
| Module | Path | Responsibility |
|---|---|---|
| health | src/modules/health |
Liveness + dependency probe. |
| auth | src/modules/auth |
register / login / refresh (rotation) / logout / reset-password; refresh JWT in httpOnly cookie. |
| users | src/modules/users |
GET/PATCH /users/me, member listing GET /users. |
| sessions | src/modules/sessions |
list/revoke active sessions (refresh-token chains). |
| workspaces | src/modules/workspaces |
workspace CRUD, members, invitations, usage, ownership transfer. |
| teams | src/modules/teams |
teamspaces CRUD + membership. |
| pages | src/modules/pages |
page tree CRUD, trash/restore, versions, favorites, templates (deep copy). |
| blocks | src/modules/blocks |
block CRUD, move, batch (≤500 ops), duplicate, flat content export. |
| databases | src/modules/databases |
properties (22 types), views (9 types), records (record = page), filters/sort via lib/filter-builder, relations, formulas (validated via packages/database-engine when installed), rollups computed on read, CSV export. |
| files | src/modules/files |
presigned S3 upload sessions (SigV4 hand-rolled in s3-presign.ts), status, signed download URLs, versions. |
| comments | src/modules/comments |
threads, replies, reactions, resolve, @mention parsing. |
| search | src/modules/search |
OpenSearch hybrid (BM25 + kNN + RRF) via opensearch.ts; Postgres ILIKE fallback when OpenSearch is down; suggestions; recent searches (redis). |
| notifications | src/modules/notifications |
in-app notifications, unread count, read/read-all. |
| share | src/modules/share |
share links (public slugs), public read endpoint (no auth). |
| api-keys | src/modules/api-keys |
fk_ workspace keys — sha256 at rest, raw key shown once, scopes READ/WRITE/ADMIN. |
| webhooks | src/modules/webhooks |
webhook CRUD (AES-GCM secrets), test ping, delivery list. |
| automations | src/modules/automations |
automation CRUD + runs + manual trigger (event automation.manual). Execution is the worker's job. |
| ai | src/modules/ai |
conversations/messages + /ai/chat; 503 AI_NOT_CONFIGURED without AI_API_KEY, otherwise pluggable AiProvider (default: clearly-marked echo adapter; real adapters live in packages/ai). |
| integrations | src/modules/integrations |
integration settings CRUD (SSO/SCIM enforcement is Phase 14). |
| forms | src/modules/forms |
form CRUD, public /f/:slug + POST /f/:slug/submit (honeypot, record creation). |
| orgs | src/modules/orgs |
organizations, members, policies/retention (stored raw; enforcement later). |
Cross-cutting (src/common, src/lib)
- Auth guard (
common/guards/auth.guard.ts, global): Bearer JWT orfk_API key; whitelists public routes (/auth/*-ish,/health,/share/:slug,/f/:slug, oauth token). - Rate limit guard (
common/guards/rate-limit.guard.ts): Redis sliding window per user/ip × route class (RATE_LIMIT_AUTHvsRATE_LIMIT_API),429 RATE_LIMITED+Retry-After. Degrades open if Redis is down. - ZodValidationPipe (
common/pipes/zod-validation.pipe.ts): body/query/ params →{error:{code:"VALIDATION_ERROR",details:[...]}}. - AllExceptionsFilter (
common/filters/all-exceptions.filter.ts): every error is{error:{code,message,details?,requestId}}; Prisma P2002→409, P2025→404; 500s are logged and generalized. - EventBus (
common/event-bus.ts):XADD factory:events(MAXLEN ~ 10000)- in-process EventEmitter mirror for tests. Event names:
page.created/updated/deleted/shared,database.created/updated,record.created/updated,comment.created,form.submitted,automation.executed,file.uploaded,api_key.created,user.login/logout/register,permission.changed,workspace.created,automation.manual.
- in-process EventEmitter mirror for tests. Event names:
- AuditService (
common/audit.service.ts):AuditLogrow per mutating endpoint (actor type USER/API_KEY/SYSTEM, ip, UA, metadata). - authorize.ts (
src/lib/authorize.ts): effective level = workspace role (Owner/Admin→Full, Member→Edit, Guest→View) maxed againstPermissionrows (user direct / team grants / resource-scoped; most specific resource wins); levelsNone < View < Comment < Edit < Full. - positions.ts (
src/lib/positions.ts): base-62 fractional positions (LexoRank-style) with growing-precision midpoints — arbitrary nesting depth. - jwt.ts (
src/lib/jwt.ts): HS256 sign/verify,node:cryptoonly. - crypto.ts (
src/lib/crypto.ts): AES-256-GCM, sha256, random tokens.