====== 04 - API First: el contrato antes del código ======
En el [[proyecto:03-partida|hito de partida]] la aplicación se construyó primero en código y el contrato quedó implícito: nadie ajeno al código podía conocer ''GET /api/products'' sin leer su implementación. Este hito aplica el [[temario:02-api-first|tema 02 — API First]]: toda la API del módulo de catálogo — ahora con **catálogos** y con la **paginación** que el listado plano ya necesita — se **diseña y se revisa antes** de implementarla, y el contrato — un documento **OpenAPI** — pasa a ser la fuente de verdad.
^ Pieza ^ Cambio en este hito ^
| Contrato | Nace ''openapi.yaml'' (v0.1.0): catálogos, productos, listado paginado y reglas 409 |
| ''controller'' | El listado plano de la partida desaparece; las operaciones nuevas se implementan desde el contrato |
| Herramientas | El esqueleto se genera con **openapi-generator**; **Swagger UI** (**springdoc**) lo hace navegable y ejecutable |
===== 1. Diseñar el documento =====
- Redactar ''openapi.yaml'' con la API completa del módulo de catálogo:
^ Operación ^ Petición ^ Respuestas ^
| Listado de catálogos | ''GET /api/catalogs'' | 200 · ''Catalog''[] |
| Ficha de catálogo | ''GET /api/catalogs/{id}'' | 200 · 404 |
| Alta de catálogo | ''POST /api/catalogs'' | 201 |
| Modificación de catálogo | ''PUT /api/catalogs/{id}'' | 200 · 404 |
| Borrado de catálogo | ''DELETE /api/catalogs/{id}'' | 204 · 404 · 409 |
| Productos de un catálogo | ''GET /api/catalogs/{id}/products?page&size'' | 200 · 404 |
| Ficha de producto | ''GET /api/products/{id}'' | 200 · 404 |
| Alta de producto | ''POST /api/products'' | 201 · 404 |
| Modificación de producto | ''PUT /api/products/{id}'' | 200 · 404 |
| Borrado de producto | ''DELETE /api/products/{id}'' | 204 · 404 |
- Esquemas: ''Catalog'' y ''CatalogRequest''; ''Product'' (respuesta, con ''id'' y ''catalogId'') y ''ProductRequest'' (con ''catalogId'', sin ''id''); ''ProductPage'' para el listado paginado del catálogo — ''content'' más los metadatos ''page'', ''size'', ''totalElements'' y ''totalPages''; y ''Problem'' (RFC 9457: ''type'', ''title'', ''status'', ''detail'') para los errores. Los 404 de catálogo o producto inexistente y los **409** de estado — nombre de catálogo repetido, catálogo con productos — se declaran también en el contrato.
- La paginación se define en el contrato: ''page'' (desde 0, por defecto 0) y ''size'' (por defecto 10) como parámetros de ''GET /api/catalogs/{id}/products''; la respuesta lleva siempre los metadatos.
- Aún **sin** constraints de validación: llegan en el [[proyecto:05-validacion|hito 05]]; la especificación de este hito describe las operaciones y sus errores de estado, no el formato de los datos.
- Esta primera versión se redacta y se **revisa antes de generar código**: es el momento de acordar el contrato.
- El documento se guarda en ''src/main/resources/openapi.yaml'', se versiona en el repositorio y su ''info.version'' sube en cada hito que toque la API.
El listado plano de la partida desaparece: veintiocho productos en una sola respuesta no escala y no deja navegar por catálogos. El contrato lo sustituye por el subrecurso paginado ''GET /api/catalogs/{id}/products''. Es la primera decisión que el contrato toma sobre el código.
===== 2. Generar desde el contrato =====
* El generador **openapi-generator** — integrado como plugin de Maven del modo que muestra el [[temario:02-api-first|tema 02]] — produce, como **código fuente que nunca se edita a mano**, las interfaces de la API y los elementos del esquema del contrato.
* Regenerar con ''mvn compile'' tras cada cambio del ''openapi.yaml'': el esqueleto resultante es la guía de lo que queda por implementar.
===== 3. Implementar desde el contrato =====
* Cada operación nueva baja por las capas de la partida: métodos nuevos en los servicios y ''SQL'' de inserción, modificación o borrado en la persistencia **JDBC**; el mecanismo sigue siendo el de 1.º DAW.
* El listado de productos de un catálogo sale paginado: el corte y los metadatos (''page'', ''size'', ''totalElements'', ''totalPages'') se resuelven con **JDBC** a mano — sin **Spring Data**, que llega con el [[proyecto:07-spring-jpa|hito 07]]. El contrato exige la respuesta completa con sus metadatos en cada listado.
* Los estados declarados en el contrato se cumplen: **404** para catálogo o producto inexistente y para el producto cuyo ''catalogId'' apunta a un catálogo que no existe; **409** si el nombre del catálogo se repite al crear o modificar, y si se intenta borrar un catálogo con productos (la comprobación, antes del borrado).
===== 4. Swagger UI =====
* **springdoc** monta la doble vista del contrato: el documento generado desde el código en ''/v3/api-docs'' y la UI ejecutable en ''/swagger-ui.html'', para revisar y probar cada operación.
===== Comprobación =====
* Swagger UI muestra catálogos y productos y permite ejecutar cada operación, incluida la paginación.
* Las operaciones responden según el contrato (200 / 201 / 204 / 404 / 409 donde toque).
* El listado paginado devuelve los metadatos correctos: el catálogo ''Periféricos'' (12 productos) reparte en 2 páginas con ''size'' 10.
* Borrar un catálogo con productos responde 409; repetir su nombre también.
* El esqueleto del contrato — interfaces y esquemas — está generado y no se ha editado a mano.
* El ''openapi.yaml'' está en el repositorio con ''info.version'' 0.1.0.
* Commit y tag ''hito-04''.
A partir de ahora el contrato manda: todo cambio de la API se escribe primero en ''openapi.yaml'' y se regenera. Los hitos que no tocan la API no lo tocan.
**Siguiente hito:** [[proyecto:05-validacion|hito 05 — Validación e invariancias]].