El cambio estructural: de paths a requests

En OpenAPI 3.1, la estructura top-level gira alrededor de paths:

# OpenAPI 3.1, centrado en paths
openapi: "3.1.0"
paths:
 /users/{userId}:
 get:
 operationId: getUser
 parameters:
 - name: userId
 in: path
 required: true
 schema:
 type: string
 responses:
 "200":
 content:
 application/json:
 schema:
 $ref: "#/components/schemas/User"

OpenAPI 4.0 reorganiza alrededor de requests y pathItems:

# OpenAPI 4.0, centrado en requests
openapi: "4.0.0"
info:
 title: User Service
 version: "2.0"

pathItems:
 user:
 parameters:
 userId:
 in: path
 schema:
 type: string
 requests:
 getUser:
 method: get
 outcomes:
 success:
 status: 200
 contentType: application/json
 body:
 schema:
 $ref: "#/components/schemas/User"
 notFound:
 status: 404
 contentType: application/problem+json
 body:
 schema:
 $ref: "#/components/schemas/Problem"

urls:
 /users/{userId}: { $ref: "#/pathItems/user" }
 /v2/users/{userId}: { $ref: "#/pathItems/user" }

Observar varias cosas. Primero, los pathItems están desacoplados de las URLs. El mismo pathItem user sirve tanto /users/{userId} como /v2/users/{userId}. Segundo, las responses ahora son outcomes, resultados semánticos nombrados en vez de entradas indexadas por status code. success y notFound son más significativos que "200" y "404".

Overlays: capas sin forkear

Esta es una feature largamente esperada. Los overlays te permiten tomar un documento base de OpenAPI y aplicar modificaciones sin editar el original:

# base.yaml, tu spec canónica
openapi: "4.0.0"
info:
 title: Payment API
 version: "1.0"
pathItems:
 charge:
 requests:
 createCharge:
 method: post
 body:
 schema:
 $ref: "#/components/schemas/ChargeRequest"
 outcomes:
 created:
 status: 201
 body:
 schema:
 $ref: "#/components/schemas/Charge"
urls:
 /charges: { $ref: "#/pathItems/charge" }
# overlay-production.yaml
overlay: "1.0.0"
extends: base.yaml
actions:
 - target: "$.servers"
 update:
 - url: https://api.example.com
 description: Production US-East
 - target: "$.pathItems.charge.requests.createCharge"
 update:
 security:
 - oauth2:
 scopes: ["charges:write"]

La spec final se construye para cada ambiente:

redocly bundle base.yaml --overlay overlay-production.yaml -o production-spec.yaml
redocly bundle base.yaml --overlay overlay-staging.yaml -o staging-spec.yaml

Esto elimina el patrón de "copiar la spec y cambiar la URL del server" que plaga toda API multi-ambiente.

Webhooks de primera clase

En 4.0 están integrados en el modelo de request/outcome:

webhooks:
 paymentCompleted:
 requests:
 notify:
 method: post
 contentType: application/json
 body:
 schema:
 type: object
 required: [event, data]
 properties:
 event:
 type: string
 const: payment.completed
 data:
 $ref: "#/components/schemas/Payment"
 outcomes:
 acknowledged:
 status: 200
 retry:
 status: [500, 502, 503]
 delivery:
 retryPolicy:
 maxRetries: 5
 backoff: exponential
 initialDelay: 30s
 signing:
 algorithm: hmac-sha256
 headerName: X-Webhook-Signature

La sección delivery es nueva, documenta políticas de reintento y mecanismos de firmado como parte de la spec.

Migración desde 3.1

La migración de una spec de OpenAPI 3.1 de 2.400 líneas a 4.0 sigue este proceso:

npx @redocly/cli transform openapi-3.1.yaml --to 4.0 -o openapi-4.0.yaml
npx @redocly/cli lint openapi-4.0.yaml --ruleset recommended

El conversor automático maneja ~85% de la transformación. El 15% restante es nombrar outcomes semánticamente, extraer pathItems compartidos, y agregar metadata de delivery de webhooks.

Patrones de diseño de spec

Patrón: Outcomes de error compartidos

components:
 outcomes:
 validationError:
 status: 422
 contentType: application/problem+json
 body:
 schema:
 $ref: "#/components/schemas/ValidationProblem"
 unauthorized:
 status: 401
 contentType: application/problem+json
 body:
 schema:
 $ref: "#/components/schemas/AuthProblem"

pathItems:
 charge:
 requests:
 createCharge:
 method: post
 outcomes:
 created:
 status: 201
 body:
 schema:
 $ref: "#/components/schemas/Charge"
 validation:
 $ref: "#/components/outcomes/validationError"
 auth:
 $ref: "#/components/outcomes/unauthorized"

Outcomes compartidos eliminan el problema de "copiar error responses a cada endpoint".

¿Deberías migrar ahora?

Evaluación honesta: para proyectos de API nuevos y tu toolchain soporta 4.0, usalo. Las mejoras estructurales hacen que las specs sean más fáciles de mantener a medida que crecen. Si teners una spec 3.1 grande que funciona bien, esperá 6-12 meses para que el tooling madure.

La mayor ganancia son los overlays. Pasar de mantener cuatro copias de una spec (dev, staging, prod, partner) a una base más cuatro archivos de overlay de 20 líneas representa una mejora significativa. Eso solo justifica el esfuerzo de migración.