You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(client): improve error handling, documentation, and response models
- Fixed RestClient defaultStatusHandler to no longer swallow exceptions;
all non-2xx responses now raise ClientProblemException with parsed or fallback ProblemDetail.
- Refactored ClientProblemException to produce structured, readable messages
and include RFC 9457 fields (type, instance, errorCode).
- Enhanced ProblemDetailSupport to safely parse or fallback on malformed bodies.
- Added Javadoc and helper factory methods to Meta (now(), with Sort, SortField, SortDirection)
for easier creation and future-proof extensibility.
- Added Javadoc to ProblemExtensions to clarify its purpose as a flexible
container for RFC 9457 extensions (e.g., traceId, pagination, metadata).
- Updated README and documentation examples to reflect corrected handler behavior.
- Verified behavior with MockWebServer and RestClient integration tests.
This completes the 0.7.1 stabilization — behavior is now consistent, explicit, and fully RFC 9457 compliant.
<em>End-to-end generics-aware OpenAPI clients — unified <code>{ data, meta }</code> responses without boilerplate.</em>
16
+
<em><strong>End-to-end generics-aware OpenAPI clients</strong> — unified <code>{ data, meta }</code> responses without boilerplate.</em>
17
17
</p>
18
18
19
19
**Modern, type-safe OpenAPI client generation** — powered by **Spring Boot 3.4**, **Java 21**, and **OpenAPI Generator 7.16.0**.
20
-
This repository demonstrates a production-grade architecture where backend and client are fully aligned through generics, enabling nested generic envelopes (`ServiceResponse<Page<T>>`) and [**RFC 7807 — Problem Details for HTTP APIs**](https://datatracker.ietf.org/doc/html/rfc7807)-based error handling.
20
+
This repository demonstrates a production-grade architecture where backend and client are fully aligned through generics, enabling nested generic envelopes (`ServiceResponse<Page<T>>`) and [**RFC 9457 — Problem Details for HTTP APIs**](https://www.rfc-editor.org/rfc/rfc9457)-based error handling.
21
+
22
+
> 🧠 **RFC 9457 vs RFC 7807**
23
+
> RFC 9457 supersedes 7807 and standardizes `application/problem+json` / `application/problem+xml` for HTTP APIs.
24
+
> Spring Framework 6+ implements this via the built-in `ProblemDetail` class, enabling consistent error serialization across server and client.
21
25
22
26
---
23
27
@@ -36,41 +40,45 @@ This repository demonstrates a production-grade architecture where backend and c
> *A clean architecture pattern for building generics-aware OpenAPI clients that stay fully type-safe, consistent, and
40
-
boilerplate-free.*
43
+
> *A clean architecture pattern for building generics-aware OpenAPI clients that stay fully type-safe, consistent, and boilerplate-free.*
41
44
42
45
---
43
46
44
47
## 📦 Modules
45
48
46
49
*[**customer-service**](customer-service/README.md) — sample backend exposing `/v3/api-docs.yaml` via Springdoc
47
-
*[**customer-service-client**](customer-service-client/README.md) — generated OpenAPI client with generics-aware
48
-
wrappers
50
+
*[**customer-service-client**](customer-service-client/README.md) — generated OpenAPI client with generics-aware wrappers
49
51
50
52
---
51
53
52
54
## 🚀 Problem & Motivation
53
55
54
56
OpenAPI Generator, by default, does not handle **generic response types**.
55
57
56
-
When backend APIs wrap payloads in `ServiceResponse<T>` (e.g., the unified `{ data, meta }` envelope),
57
-
the generator produces **duplicated models per endpoint** instead of a single reusable generic base.
58
+
When backend APIs wrap payloads in `ServiceResponse<T>` (e.g., the unified `{ data, meta }` envelope), the generator produces **duplicated models per endpoint** instead of a single reusable generic base.
58
59
59
60
This results in:
60
61
61
62
* ❌ Dozens of almost-identical response classes
62
63
* ❌ Higher maintenance overhead
63
64
* ❌ Harder to evolve a single envelope contract across services
64
65
66
+
```java
67
+
// Default OpenAPI output (before)
68
+
classCreateCustomerResponse { CustomerDto data; Meta meta; }
69
+
classUpdateCustomerResponse { CustomerDto data; Meta meta; }
70
+
// ... dozens of duplicates
71
+
```
72
+
65
73
---
66
74
67
75
## 💡 Solution Overview
68
76
69
-
This project provides a **full-stack pattern**to align Spring Boot services and OpenAPI clients.
77
+
This project provides a **full-stack pattern**aligning Spring Boot services and OpenAPI clients through automatic schema introspection and template overlay.
70
78
71
-
### Server-Side (Producer)
79
+
### 🖥️ Server-Side (Producer)
72
80
73
-
A `Springdoc` customizer automatically inspects controller return types like:
81
+
A `Springdoc` customizer inspects controller return types such as:
This pattern supports **nested generics** like `ServiceClientResponse<Page<CustomerDto>>` and automatically maps all error responses into **ProblemDetail** objects.
123
+
✅ Supports **nested generics** like `ServiceClientResponse<Page<CustomerDto>>`
124
+
✅ Automatically maps error responses into **RFC 9457 Problem Details**
Each wrapper extends `ServiceClientResponse<T>` and aligns perfectly with the `{ data, meta }` envelope model.
162
+
Each wrapper extends `ServiceClientResponse<T>` and aligns with the unified `{ data, meta }` envelope.
157
163
158
-
You can now test end-to-end type-safe responses through the generated client — verifying both single and paged envelopes in action.
164
+
Now you can test end-to-end type-safe responses via the generated client — validating both single and paged envelopes.
159
165
160
166
---
161
167
162
168
## 🔄 Generated Wrappers — Before & After
163
169
164
-
Comparison of how OpenAPI Generator outputs looked **before** vs **after** enabling the generics-aware wrapper support.
165
-
166
-
**Before (duplicated full model):**
170
+
**Before (duplicated full models):**
167
171
168
172
<p align="center">
169
173
<img src="docs/images/generated-client-wrapper-before.png" alt="Generated client before generics support" width="800"/>
170
174
<br/>
171
-
<em>Each endpoint generated its own full response model — duplicated <code>data</code> and <code>meta</code> fields across classes.</em>
175
+
<em>Each endpoint generated its own response class, duplicating <code>data</code> and <code>meta</code> fields.</em>
172
176
</p>
173
177
174
178
**After (thin generic wrapper):**
175
179
176
180
<p align="center">
177
181
<img src="docs/images/generated-client-wrapper-after.png" alt="Generated client after generics support" width="800"/>
178
182
<br/>
179
-
<em>Now every endpoint extends the reusable <code>ServiceClientResponse<Page<T>></code> base, eliminating boilerplate and preserving type safety.</em>
183
+
<em>Each endpoint now extends the reusable <code>ServiceClientResponse<Page<T>></code> base, eliminating boilerplate and preserving type safety.</em>
180
184
</p>
181
185
182
-
---
186
+
---
183
187
184
188
## 🧱 Example Responses
185
189
186
-
The unified envelope applies to both single and paged responses.
190
+
Unified envelope structure applies to both single and paged results.
187
191
188
192
### 🧩 Single Item Example (`ServiceClientResponse<CustomerDto>`)
189
193
@@ -207,16 +211,8 @@ The unified envelope applies to both single and paged responses.
* 📘 [Medium — We Made OpenAPI Generator Think in Generics](https://medium.com/@baris.sayli/type-safe-generic-api-responses-with-spring-boot-3-4-openapi-generator-and-custom-templates-ccd93405fb04)
297
+
* 📰 [Medium — We Made OpenAPI Generator Think in Generics](https://medium.com/@baris.sayli/type-safe-generic-api-responses-with-spring-boot-3-4-openapi-generator-and-custom-templates-ccd93405fb04)
309
298
* 💬 [Dev.to — We Made OpenAPI Generator Think in Generics](https://dev.to/barissayli/spring-boot-openapi-generator-type-safe-generic-api-clients-without-boilerplate-3a8f)
299
+
* 📘 [RFC 9457 — Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457)
300
+
310
301
---
311
302
312
303
## 🛡 License
@@ -317,14 +308,14 @@ Licensed under **MIT** — see [LICENSE](LICENSE).
317
308
318
309
## 💬 Feedback
319
310
320
-
If you spot an error or have suggestions, open an issue or join the discussion — contributions are welcome.
311
+
If you spot an error or have suggestions, open an issue or join the discussion — contributions are welcome.
321
312
💭 [Start a discussion →](https://github.com/bsayli/spring-boot-openapi-generics-clients/discussions)
322
313
323
314
---
324
315
325
316
## 🤝 Contributing
326
317
327
-
Contributions, issues, and feature requests are welcome!
318
+
Contributions, issues, and feature requests are welcome!
328
319
Feel free to [open an issue](https://github.com/bsayli/spring-boot-openapi-generics-clients/issues) or submit a PR.
329
320
330
321
---
@@ -335,5 +326,5 @@ If you found this project helpful, please give it a ⭐ on GitHub — it helps o
0 commit comments