feat(api-skeleton): fastify scaffold with envelope, error mapper, rate limit, idempotency, OpenAPI#17
Merged
Merged
Conversation
themightychris
added a commit
that referenced
this pull request
May 16, 2026
- status: done, pr: 17 - All 10 validation criteria ticked (CI green: type-check + lint + test + build pass) - Notes: @fastify/env JSON Schema vs Zod dual-maintenance; /api/_openapi.json manual route (swagger-ui serves at /api/_docs/json); pluginTimeout 30s for git cold-read; vitest timeout 30s for worktree git ops; rate limit account-keying stubbed pending auth; /_test/* routes in production - Follow-ups: - Issue #18: guard /_test/* stub routes in production - auth-jwt-substrate plan updated to absorb account-based rate-limit wiring
npm install --workspace=apps/api @fastify/env @fastify/cors @fastify/cookie @fastify/swagger @fastify/swagger-ui @fastify/rate-limit uuidv7 zod
…it, idempotency, OpenAPI - env.ts: Zod + JSON Schema env validation; all process.env reads isolated here - lib/response.ts: ok() / paginated() / errorResponse() envelope helpers - lib/errors.ts: custom error classes + setErrorHandler mapper (gitsheets errors + our own) - plugins/trace-id.ts: UUIDv7 traceId decorator on every request - plugins/store.ts: decorates fastify.store from bootStores() - plugins/rate-limit.ts: in-memory counters per-IP (60 reads, 30 writes, 10 auth/min) - plugins/idempotency.ts: 24h in-memory cache keyed by (personId, Idempotency-Key) - routes/health.ts: GET /api/health + /_test/* stubs for error/idempotency tests - app.ts: buildApp() wires plugins in specified order; accepts overrideEnv for tests - index.ts: updated entry point that calls buildApp() - .env.example: all EnvSchema fields with inline comments - /api/_openapi.json and /api/_docs served via @fastify/swagger + @fastify/swagger-ui Plugin order: env → cors → cookie → trace-id → setErrorHandler → store → rate-limit → idempotency → swagger → swagger-ui → routes
- tests/api-skeleton.test.ts: covers all 10 Validation criteria from the plan (health envelope, error mapper, 500 no-leak, traceId UUIDv7, rate limit 61→429, idempotency key replay, OpenAPI JSON, Swagger UI, env validation rejects) - tests/helpers/test-full-repo.ts: createFullDataRepo() creates a gitsheets repo with all required sheet configs for full-app tests using openPublicStore() - vitest.config.ts: testTimeout + hookTimeout bumped to 30s (git ops in worktrees run slower than the default 5s avvio/vitest timeouts)
These were installed at the workspace root but not declared in apps/api/package.json, causing the type-check to fail in CI where each workspace's dependencies are resolved strictly from its own package.json.
- status: done, pr: 17 - All 10 validation criteria ticked (CI green: type-check + lint + test + build pass) - Notes: @fastify/env JSON Schema vs Zod dual-maintenance; /api/_openapi.json manual route (swagger-ui serves at /api/_docs/json); pluginTimeout 30s for git cold-read; vitest timeout 30s for worktree git ops; rate limit account-keying stubbed pending auth; /_test/* routes in production - Follow-ups: - Issue #18: guard /_test/* stub routes in production - auth-jwt-substrate plan updated to absorb account-based rate-limit wiring
dd40c7b to
19eef70
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
buildApp()inapps/api/src/app.ts) with the complete plugin stack defined in the api-skeleton planok(),paginated(),errorResponse()) and a singlesetErrorHandlerthat maps gitsheets errors + custom API errors to the documented error codes/api/_openapi.json+ Swagger UI at/api/_docsGET /api/healthis the only business endpoint; all other stubs are/_test/*routes used only by integration tests.env.examplewith inline comments for everyEnvSchemafieldTest plan
GET /api/healthreturns{ success: true, data: { status: 'ok' }, metadata: { timestamp } }STORAGE_BACKENDthrows (env validation rejects)CFP_DATA_REPO_PATHthrowsApiValidationErrorsurfaces as422 validation_failedwitherror.traceIdanderror.fields500 internal_errorwith no error message leakedtraceIdin error responses matches UUIDv7 format (/^[0-9a-f]{8}-...-7.../)Retry-AfterIdempotency-Keyreturns the cached response (frozenattimestamp)/api/_openapi.jsonreturns a valid OpenAPI 3.1 document (openapi: "3.1.*")/api/_docsresponds < 400🤖 Generated with Claude Code