Files
familienarchiv/.specify/templates/api-contract-stub.md
Marcel 01f51854f6 feat(sdd): add .specify scaffold — constitution, AGENTS, personas, templates, example, RTM
Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md
grounded in the project's real conventions; six EARS-aware persona spec-review
checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/
api-contract templates; a fully worked _example feature; a living RTM; and an
adrs/ pointer that reuses the existing docs/adr/ archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:56:31 +02:00

95 lines
3.9 KiB
Markdown

# API Contract Stub
This project is **REST + OpenAPI**. The backend serves the live spec via springdoc at
`http://localhost:8080/v3/api-docs` (dev profile only), and the frontend generates its
TypeScript client from it with `npm run generate:api` (`openapi-typescript`
`frontend/src/lib/generated/api.ts`). There is no GraphQL in this stack.
> **The live spec is generated from the Java controllers — it is the source of truth.** A
> hand-written contract under `.specify/features/<name>/api-contract.yaml` is a *design
> artifact*: it pins the intended shape during spec review and is checked against the
> generated spec once the endpoint exists. Keep it OpenAPI **3.1**, and keep
> `@Schema(requiredMode = REQUIRED)` on the Java side as the real driver of `required`.
## How to use this stub
1. Copy the skeleton below to `.specify/features/<name>/api-contract.yaml`.
2. Fill in the paths/methods/schemas your feature adds. Every mutating path documents the
`403`/`401` responses and the `cookieAuth` security requirement (matching the real
`@RequirePermission` gate).
3. The `sdd-gate.yml` CI job lints any changed `api-contract.yaml` with Spectral
(`npx @stoplight/spectral-cli lint`). Run it locally the same way before pushing.
4. After the endpoint ships, run `npm run generate:api` and diff the generated types against
this contract; reconcile any drift (the generated spec wins — update the contract).
## OpenAPI 3.1 skeleton
```yaml
openapi: 3.1.0
info:
title: Familienarchiv API — <feature name>
version: 0.0.1-SNAPSHOT
description: Design-time contract for <feature>. Source of truth is the generated /v3/api-docs.
servers:
- url: http://localhost:8080
description: Local backend (dev profile)
- url: https://archiv.raddatz.cloud
description: Production (behind Caddy)
components:
securitySchemes:
cookieAuth: # Spring Session JDBC — opaque session id in the SESSION cookie
type: apiKey
in: cookie
name: SESSION
schemas:
ErrorResponse: # shape produced by GlobalExceptionHandler
type: object
required: [code, message]
properties:
code:
type: string
description: Machine-readable ErrorCode (see ErrorCode.java / errors.ts).
example: FORBIDDEN
message:
type: string
# <YourResponseView>: # always a view, never a lazy-collection entity (ADR-036)
# type: object
# required: [id]
# properties:
# id: { type: string, format: uuid }
security:
- cookieAuth: [] # default: every path requires a session unless overridden to []
paths:
/api/<resource>:
post:
summary: <create …>
operationId: <createResource>
security:
- cookieAuth: [] # plus @RequirePermission(Permission.X) on the controller
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/<CreateDTO>' }
responses:
'201':
description: Created
content:
application/json:
schema: { $ref: '#/components/schemas/<YourResponseView>' }
'400': { description: Validation failed, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'401': { description: Unauthenticated, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
'403': { description: Missing permission, content: { application/json: { schema: { $ref: '#/components/schemas/ErrorResponse' } } } }
```
## Validating the contract in CI
The `sdd-gate.yml` workflow runs, on PRs that touch a `api-contract.yaml`:
```bash
npx @stoplight/spectral-cli lint .specify/features/**/api-contract.yaml
```
Spectral's default OpenAPI ruleset catches malformed specs, missing `operationId`s, and
undefined `$ref`s. Add a `.spectral.yaml` at the repo root to tune rules if needed.