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

3.9 KiB

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-typescriptfrontend/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

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:

npx @stoplight/spectral-cli lint .specify/features/**/api-contract.yaml

Spectral's default OpenAPI ruleset catches malformed specs, missing operationIds, and undefined $refs. Add a .spectral.yaml at the repo root to tune rules if needed.