|
| 1 | +# Client-Side Integration Guide |
| 2 | + |
| 3 | +This document describes how to integrate the **generics-aware OpenAPI client** into your own microservice project, based on the patterns demonstrated in the `customer-service-client` module. |
| 4 | + |
| 5 | +The purpose is to generate **type-safe clients** that extend a reusable generic base (`ServiceClientResponse<T>`) instead of duplicating response envelopes. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 1) What You Get |
| 10 | + |
| 11 | +* Thin wrappers per endpoint (`ServiceResponseYourDto`), each extending `ServiceClientResponse<T>`. |
| 12 | +* Strong typing for `getData()` without boilerplate or casting. |
| 13 | +* A reusable adapter interface to keep generated code isolated. |
| 14 | +* Optional Spring Boot configuration to auto-wire the client beans. |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## 2) Prerequisites |
| 19 | + |
| 20 | +* Java 21+ |
| 21 | +* Maven 3.9+ (or Gradle 8+ if adapted) |
| 22 | +* Running service exposing `/v3/api-docs.yaml` |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## 3) Steps to Generate |
| 27 | + |
| 28 | +1. **Pull the OpenAPI spec** from your service: |
| 29 | + |
| 30 | + ```bash |
| 31 | + curl -s http://localhost:8084/your-service/v3/api-docs.yaml \ |
| 32 | + -o src/main/resources/your-api-docs.yaml |
| 33 | + ``` |
| 34 | + |
| 35 | +2. **Run Maven build** in the client module: |
| 36 | + |
| 37 | + ```bash |
| 38 | + mvn clean install |
| 39 | + ``` |
| 40 | + |
| 41 | +3. **Inspect generated code**: |
| 42 | + |
| 43 | + * `target/generated-sources/openapi/src/gen/java` |
| 44 | + * Look for classes like `ServiceResponseYourDto` extending `ServiceClientResponse<YourDto>` |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## 4) Core Classes to Copy |
| 49 | + |
| 50 | +Ensure you copy the following **shared classes** into your client project: |
| 51 | + |
| 52 | +**`common/ServiceClientResponse.java`** |
| 53 | + |
| 54 | +```java |
| 55 | +package <your.base>.openapi.client.common; |
| 56 | + |
| 57 | +import java.util.List; |
| 58 | +import java.util.Objects; |
| 59 | + |
| 60 | +public class ServiceClientResponse<T> { |
| 61 | + private Integer status; |
| 62 | + private String message; |
| 63 | + private List<ClientErrorDetail> errors; |
| 64 | + private T data; |
| 65 | + // getters, setters, equals, hashCode, toString |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +**`common/ClientErrorDetail.java`** |
| 70 | + |
| 71 | +```java |
| 72 | +package <your.base>.openapi.client.common; |
| 73 | + |
| 74 | +public record ClientErrorDetail(String errorCode, String message) {} |
| 75 | +``` |
| 76 | + |
| 77 | +These are referenced by the Mustache templates and must exist in your client project. |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## 5) Mustache Templates |
| 82 | + |
| 83 | +Place the following templates under: |
| 84 | + |
| 85 | +``` |
| 86 | +src/main/resources/openapi-templates/ |
| 87 | +``` |
| 88 | + |
| 89 | +**`api_wrapper.mustache`** |
| 90 | + |
| 91 | +```mustache |
| 92 | +{{#vendorExtensions.x-class-extra-annotation}} |
| 93 | +{{{vendorExtensions.x-class-extra-annotation}}} |
| 94 | +{{/vendorExtensions.x-class-extra-annotation}} |
| 95 | +public class {{classname}} |
| 96 | + extends {{commonPackage}}.ServiceClientResponse<{{vendorExtensions.x-api-wrapper-datatype}}> { |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +**`model.mustache`** (partial overlay to delegate wrapper classes to `api_wrapper.mustache`). |
| 101 | + |
| 102 | +These ensure generated wrappers extend the generic base instead of duplicating fields. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## 6) Adapter Pattern |
| 107 | + |
| 108 | +Encapsulate generated APIs in an adapter interface: |
| 109 | + |
| 110 | +```java |
| 111 | +package <your.base>.openapi.client.adapter; |
| 112 | + |
| 113 | +import <your.base>.openapi.client.common.ServiceClientResponse; |
| 114 | +import <your.base>.openapi.client.generated.dto.*; |
| 115 | + |
| 116 | +public interface YourClientAdapter { |
| 117 | + ServiceClientResponse<YourDto> getYourEntity(Integer id); |
| 118 | + ServiceClientResponse<YourCreateResponse> createYourEntity(YourCreateRequest request); |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +This shields your business code from generated artifacts and provides a stable contract. |
| 123 | + |
| 124 | +--- |
| 125 | + |
| 126 | +## 7) Spring Boot Configuration (Optional) |
| 127 | + |
| 128 | +Example auto-wiring configuration: |
| 129 | + |
| 130 | +```java |
| 131 | +@Configuration |
| 132 | +public class YourApiClientConfig { |
| 133 | + |
| 134 | + @Bean |
| 135 | + RestClient yourRestClient(RestClient.Builder builder, |
| 136 | + @Value("${your.api.base-url}") String baseUrl) { |
| 137 | + return builder.baseUrl(baseUrl).build(); |
| 138 | + } |
| 139 | + |
| 140 | + @Bean |
| 141 | + ApiClient yourApiClient(RestClient yourRestClient, |
| 142 | + @Value("${your.api.base-url}") String baseUrl) { |
| 143 | + return new ApiClient(yourRestClient).setBasePath(baseUrl); |
| 144 | + } |
| 145 | + |
| 146 | + @Bean |
| 147 | + YourControllerApi yourControllerApi(ApiClient apiClient) { |
| 148 | + return new YourControllerApi(apiClient); |
| 149 | + } |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +**application.properties:** |
| 154 | + |
| 155 | +```properties |
| 156 | +your.api.base-url=http://localhost:8084/your-service |
| 157 | +``` |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## 8) Usage Example |
| 162 | + |
| 163 | +```java |
| 164 | +@Autowired |
| 165 | +private YourControllerApi yourApi; |
| 166 | + |
| 167 | +public void demo() { |
| 168 | + var req = new YourCreateRequest().name("Alice"); |
| 169 | + var resp = yourApi.createYourEntity(req); |
| 170 | + System.out.println(resp.getStatus()); |
| 171 | + System.out.println(resp.getData().getName()); |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## 9) Notes |
| 178 | + |
| 179 | +* Dependencies like `spring-web`, `jakarta.*` are often marked **provided** — your host app must supply them. |
| 180 | +* Re-run `curl` + `mvn clean install` whenever your service’s OpenAPI spec changes. |
| 181 | +* Optional vendor extension `x-class-extra-annotation` can add annotations (e.g., Jackson or Lombok) on generated wrappers. |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## 10) Folder Structure (Suggested) |
| 186 | + |
| 187 | +``` |
| 188 | +your-service-client/ |
| 189 | + ├─ src/main/java/<your/base>/openapi/client/common/ |
| 190 | + │ ├─ ServiceClientResponse.java |
| 191 | + │ └─ ClientErrorDetail.java |
| 192 | + ├─ src/main/java/<your/base>/openapi/client/adapter/ |
| 193 | + │ └─ YourClientAdapter.java |
| 194 | + ├─ src/main/resources/openapi-templates/ |
| 195 | + │ ├─ api_wrapper.mustache |
| 196 | + │ └─ model.mustache |
| 197 | + ├─ src/main/resources/your-api-docs.yaml |
| 198 | + └─ pom.xml |
| 199 | +``` |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +✅ With this setup, your client project generates **type-safe wrappers** that align with `ServiceResponse<T>` from the server side, without any boilerplate duplication. |
0 commit comments