Spectral v6 ships no implicit ruleset — the CI job exited 'no ruleset found'. Adds .spectral.yaml (extends spectral:oas, documentation-only warnings relaxed for design-time stubs), adds operation tags to the _example contract so it lints clean (0 results), and aligns the api-contract-stub note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.1 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-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 stub is a design artifact: it pins the intended shape during spec review. Issue-only: paste the stub inline into the issue's
## API / Contract Stubsection. Keep it OpenAPI 3.1, and keep@Schema(requiredMode = REQUIRED)on the Java side as the real driver ofrequired.
How to use this stub
- Fill in the skeleton below with the paths/methods/schemas your feature adds, and paste it
into the issue's
## API / Contract Stubsection. - Every mutating path documents the
403/401responses and thecookieAuthsecurity requirement (matching the real@RequirePermissiongate). - If you prefer a standalone, lintable file (e.g. for a large contract), commit it on the
feature branch as
<feature>.openapi.yaml— thesdd-gate.ymlCI job lints any committed OpenAPI contract with Spectral (npx @stoplight/spectral-cli lint). It never needs to predate the issue. - After the endpoint ships, run
npm run generate:apiand 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 contract-validate job lints any committed OpenAPI file changed in the PR:
npx @stoplight/spectral-cli lint <your-contract>.yaml
The ruleset is .spectral.yaml at the repo root (extends spectral:oas; documentation-only
warnings relaxed for design-time stubs). Spectral auto-discovers it. It catches malformed
specs, undefined $refs, and duplicate operationIds; tune .spectral.yaml to adjust.