T-5: set up API-first tooling with OpenAPI spec as single source of truth

Backend: openapi-generator-maven-plugin generates Spring interfaces and DTOs
from the spec. Frontend: openapi-typescript + openapi-fetch provide type-safe
API access. Both sides get compile-time contract enforcement from a single
api.yaml file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 18:04:55 +01:00
parent 1ed379bc1c
commit 5ad6a08b72
9 changed files with 388 additions and 10 deletions

View File

@@ -32,6 +32,11 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -96,6 +101,7 @@
<threshold>Low</threshold>
<xmlOutput>true</xmlOutput>
<failOnError>true</failOnError>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
</configuration>
<executions>
<execution>
@@ -105,6 +111,56 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.20.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapi/api.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>de.fete.adapter.in.web.api</apiPackage>
<modelPackage>de.fete.adapter.in.web.model</modelPackage>
<generateSupportingFiles>true</generateSupportingFiles>
<supportingFilesToGenerate>ApiUtil.java</supportingFilesToGenerate>
<configOptions>
<interfaceOnly>true</interfaceOnly>
<useSpringBoot3>true</useSpringBoot3>
<useBeanValidation>true</useBeanValidation>
<performBeanValidation>true</performBeanValidation>
<openApiNullable>false</openApiNullable>
<skipDefaultInterface>true</skipDefaultInterface>
<useResponseEntity>true</useResponseEntity>
<documentationProvider>none</documentationProvider>
<annotationLibrary>none</annotationLibrary>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>add-openapi-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/openapi/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<!-- Exclude generated OpenAPI code from SpotBugs analysis -->
<Match>
<Package name="de.fete.adapter.in.web.api"/>
</Match>
<Match>
<Package name="de.fete.adapter.in.web.model"/>
</Match>
</FindBugsFilter>

View File

@@ -1,4 +1,5 @@
spring.application.name=fete
server.servlet.context-path=/api
management.endpoints.web.exposure.include=health
management.endpoint.health.show-details=never

View File

@@ -0,0 +1,37 @@
openapi: 3.1.0
info:
title: fete API
description: Privacy-focused event announcements and RSVPs
version: 0.1.0
license:
name: GPL-3.0-or-later
identifier: GPL-3.0-or-later
servers:
- url: /api
paths:
/health:
get:
operationId: getHealth
summary: Health check
tags:
- health
responses:
"200":
description: Service is healthy
content:
application/json:
schema:
$ref: "#/components/schemas/HealthResponse"
components:
schemas:
HealthResponse:
type: object
required:
- status
properties:
status:
type: string
example: UP