Si trabajas con integraciones el tiempo suficiente, tarde o temprano te encontrarás con SOAP. No es una reliquia arqueológica: bancos, aseguradoras, administraciones públicas y sistemas sanitarios siguen exponiendo miles de servicios SOAP en producción, y no van a desaparecer a corto plazo. Entender qué es, por qué se diseñó así y en qué se diferencia de REST te servirá para dos cosas muy concretas: integrarte con esos sistemas cuando toque, y comprender mejor por qué REST tomó las decisiones que tomó. En esta lección veremos la misma consulta de Tienda Aroma —obtener los datos de un café— resuelta en ambos estilos, lado a lado.
Contenido
- Qué es SOAP exactamente
- La estructura del sobre: Envelope, Header y Body
- El contrato: WSDL
- La pila WS-*
- El mismo caso lado a lado: consultar un café
- Manejo de errores: SOAP Fault frente a códigos HTTP
- Tabla comparativa completa
- Cuándo sigue teniendo sentido SOAP
- Cuándo elegir REST
- Fachadas REST sobre servicios SOAP heredados
- Qué es SOAP exactamente
SOAP nació como acrónimo de Simple Object Access Protocol, aunque desde la versión 1.2 el W3C dejó de expandir las siglas (entre otras cosas, porque de simple tenía poco). A diferencia de REST, que es un estilo arquitectónico, SOAP es un protocolo: tiene una especificación formal que define exactamente cómo debe ser un mensaje válido.
Sus rasgos definitorios:
- Basado en XML: todo mensaje es un documento XML con una estructura fija.
- Independiente del transporte: puede viajar sobre HTTP, pero también sobre SMTP (correo), JMS (colas de mensajes) o TCP puro. Esta neutralidad fue un objetivo de diseño explícito.
- Orientado a operaciones, no a recursos: se invocan métodos como
obtenerCafeocrearPedido, al estilo RPC. - Contrato formal descrito en WSDL, legible por máquinas, del que se generan clientes automáticamente.
- Extensible mediante la pila WS-* para seguridad, fiabilidad y transacciones.
Cuando SOAP viaja sobre HTTP —el caso más habitual— usa siempre POST contra un único endpoint. Reconocerás el patrón: es exactamente el nivel 0 del modelo de Richardson que vimos en la lección anterior. HTTP actúa como mero túnel de transporte.
- La estructura del sobre: Envelope, Header y Body
Todo mensaje SOAP es un "sobre" con la misma anatomía:
graph TD
E["<b>Envelope</b><br/>el sobre; raíz obligatoria"] --> H["<b>Header</b><br/>opcional: seguridad, transacciones,<br/>enrutado, correlación"]
E --> B["<b>Body</b><br/>obligatorio: la llamada<br/>o su resultado"]
B --> F["<b>Fault</b><br/>dentro de Body,<br/>solo si hay error"]
- Envelope: el elemento raíz. Identifica el documento como mensaje SOAP.
- Header: opcional, contiene metadatos de infraestructura. Aquí es donde viven WS-Security (firmas, credenciales), los identificadores de transacción y el enrutado. Es el equivalente conceptual a las cabeceras HTTP, pero dentro del mensaje, lo que permite que sobrevivan a cualquier cambio de transporte.
- Body: obligatorio, contiene la carga útil: la operación invocada con sus parámetros, o el resultado.
- Fault: un elemento especial dentro de
Bodyque representa un error.
Esta separación entre Header y Body es más elegante de lo que parece: permite que un intermediario procese la seguridad del Header sin tocar el contenido de negocio, y que el mensaje firmado siga siendo verificable aunque cambie de transporte tres veces en su recorrido.
- El contrato: WSDL
WSDL (Web Services Description Language) es un documento XML que describe formalmente el servicio: qué operaciones ofrece, qué tipos de datos usa, qué mensajes se intercambian y en qué dirección está disponible.
Su valor práctico es enorme y conviene reconocerlo: a partir de un WSDL, las herramientas generan automáticamente el código cliente completo en Java, C# o el lenguaje que sea. El desarrollador escribe servicio.obtenerCafe("caf_001") y no ve un solo byte de XML.
Un fragmento simplificado del WSDL de Tienda Aroma:
<definitions name="ServicioCafes"
targetNamespace="http://tiendaaroma.example/servicios"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- 1. Tipos: la estructura exacta de los datos, validable -->
<types>
<xsd:schema targetNamespace="http://tiendaaroma.example/servicios">
<xsd:element name="ObtenerCafePeticion">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="cafeId" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="ObtenerCafeRespuesta">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="id" type="xsd:string"/>
<xsd:element name="nombre" type="xsd:string"/>
<xsd:element name="origen" type="xsd:string"/>
<xsd:element name="tueste" type="xsd:string"/>
<xsd:element name="precioEuros" type="xsd:decimal"/>
<xsd:element name="stock" type="xsd:int"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
<!-- 2. Operaciones disponibles -->
<portType name="CafesPortType">
<operation name="obtenerCafe">
<input message="tns:ObtenerCafePeticion"/>
<output message="tns:ObtenerCafeRespuesta"/>
</operation>
</portType>
<!-- 3. Dónde vive el servicio -->
<service name="ServicioCafes">
<port name="CafesPort" binding="tns:CafesBinding">
<soap:address location="https://servicios.tiendaaroma.example/cafes"/>
</port>
</service>
</definitions>Observa la fortaleza real de este enfoque: precioEuros está declarado como xsd:decimal y stock como xsd:int. Un mensaje que envíe "catorce cincuenta" es inválido y se rechaza automáticamente, sin escribir una línea de validación. En REST, ese papel lo cumplen hoy OpenAPI y JSON Schema, pero de forma opcional (lecciones 03-04 y 05-02).
- La pila WS-*
Sobre SOAP se construyó una familia de especificaciones para cubrir necesidades empresariales que HTTP no resolvía por sí mismo:
| Especificación | Qué aporta | Equivalente aproximado en el mundo REST |
|---|---|---|
| WS-Security | Firma y cifrado a nivel de mensaje, credenciales en el Header |
TLS (a nivel de canal) + JWT firmados |
| WS-ReliableMessaging | Garantía de entrega y de orden, con reintentos y acuses | Reintentos con idempotencia; colas de mensajes |
| WS-AtomicTransaction | Transacciones distribuidas entre varios servicios (confirmar todo o nada) | No existe equivalente directo: patrones saga, compensaciones |
| WS-Addressing | Direccionamiento y correlación independientes del transporte | Cabeceras HTTP y de correlación |
| WS-Policy | Declaración de requisitos (qué cifrado exige el servicio) | Documentación y configuración del gateway |
Hay dos capacidades aquí que REST realmente no iguala y conviene reconocerlo sin complejos:
- Seguridad a nivel de mensaje. TLS cifra el canal: en cada salto intermedio el mensaje se descifra. WS-Security firma y cifra el contenido, de modo que puede atravesar cinco intermediarios y seguir siendo verificable en destino. Para una orden de transferencia bancaria con validez legal, la diferencia no es teórica.
- Transacciones distribuidas. WS-AtomicTransaction permite coordinar una operación que abarca varios servicios con confirmación en dos fases. En el mundo REST se resuelve con patrones de compensación, que son más simples de operar pero ofrecen garantías más débiles.
El precio de todo esto fue la complejidad: las especificaciones se contaban por decenas, no todas las implementaciones eran compatibles entre sí, y desarrollar sin un IDE que generara el código era muy costoso.
- El mismo caso lado a lado: consultar un café
Nada aclara tanto como ver la misma operación en ambos estilos. Queremos obtener los datos del café caf_001 de Tienda Aroma.
En SOAP
La petición:
POST /servicios/cafes HTTP/1.1
Host: servicios.tiendaaroma.example
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tiendaaroma.example/servicios/obtenerCafe"
Content-Length: 412
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:tien="http://tiendaaroma.example/servicios">
<soap:Header>
<tien:Credenciales>
<tien:usuario>panel_interno</tien:usuario>
<tien:token>eyJhbGciOiJIUzI1NiJ9...</tien:token>
</tien:Credenciales>
</soap:Header>
<soap:Body>
<tien:ObtenerCafePeticion>
<tien:cafeId>caf_001</tien:cafeId>
</tien:ObtenerCafePeticion>
</soap:Body>
</soap:Envelope>La respuesta:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 498
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:tien="http://tiendaaroma.example/servicios">
<soap:Body>
<tien:ObtenerCafeRespuesta>
<tien:id>caf_001</tien:id>
<tien:nombre>Etiopía Yirgacheffe</tien:nombre>
<tien:origen>Etiopía</tien:origen>
<tien:tueste>claro</tien:tueste>
<tien:precioEuros>14.50</tien:precioEuros>
<tien:stock>120</tien:stock>
</tien:ObtenerCafeRespuesta>
</soap:Body>
</soap:Envelope>Cosas que merece la pena señalar:
- Se usa
POSTaunque la operación solo lee datos. Consecuencia: ninguna caché intermedia puede reutilizar esta respuesta. - El endpoint es único (
/servicios/cafes); el café concreto va en el cuerpo. No hay una URL que identifique acaf_001, así que no se puede enlazar ni marcar. - La cabecera
SOAPActionindica qué operación se invoca. Es un mecanismo propio de SOAP, ajeno a HTTP. - Los espacios de nombres (
xmlns) evitan colisiones entre vocabularios, a costa de mucho ruido visual. - La respuesta es
200 OKincluso cuando hay error de negocio: el resultado real está en el cuerpo.
En REST
La petición:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json" \
https://api.tiendaaroma.example/v1/cafes/caf_001En crudo:
GET /v1/cafes/caf_001 HTTP/1.1
Host: api.tiendaaroma.example
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...La respuesta:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=300
ETag: "a7f3c9"
{
"id": "caf_001",
"nombre": "Etiopía Yirgacheffe",
"origen": "Etiopía",
"tueste": "claro",
"precioEuros": 14.50,
"stock": 120
}La comparación de tamaños es demoledora: unos 900 bytes de ida y vuelta en SOAP frente a unos 200 en REST, para exactamente la misma información. En una app móvil que consulta el catálogo cientos de veces, eso se traduce en datos, batería y tiempo.
Pero el ahorro de bytes no es lo más importante. Lo decisivo es que en REST:
- El café tiene una URL propia que se puede compartir, enlazar y probar desde el navegador.
- La operación es un
GET, así que es cacheable (max-age=300) y segura de reintentar. - El resultado se comunica con el código de estado del propio protocolo.
- Cualquiera puede probarlo con
curlen diez segundos, sin generar código ni instalar nada.
- Manejo de errores: SOAP Fault frente a códigos HTTP
Cuando el café no existe, SOAP responde con un Fault:
HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset=utf-8
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>soap:Sender</soap:Value>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="es">El café solicitado no existe</soap:Text>
</soap:Reason>
<soap:Detail>
<tien:CodigoError>CAFE_NO_ENCONTRADO</tien:CodigoError>
<tien:CafeId>caf_999</tien:CafeId>
</soap:Detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>Nota la incoherencia que arrastra el modelo: el error es del cliente (ha pedido un café inexistente), pero HTTP devuelve 500, que significa "error del servidor". SOAP 1.1 obligaba a esto; SOAP 1.2 lo flexibilizó, pero el patrón sigue vivo en muchos servicios. El resultado es que la monitorización basada en códigos HTTP no sirve: hay que abrir el XML para saber qué pasó.
En REST, el mismo error:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": {
"codigo": "cafe_no_encontrado",
"mensaje": "No existe ningún café con el identificador caf_999"
}
}El código de estado ya lo dice todo para las máquinas, y el cuerpo aporta el detalle para las personas. Un panel de monitorización distingue de un vistazo entre "los clientes piden cosas que no existen" (4xx) y "mi servicio está roto" (5xx). El diseño detallado de errores lo veremos en 02-04 y 03-07.
| Aspecto | SOAP Fault | Códigos HTTP |
|---|---|---|
| Dónde vive el error | En el cuerpo XML | En la línea de estado |
| Distingue culpa | Sender / Receiver dentro del XML |
4xx / 5xx, visible sin parsear |
| Visible para intermediarios | No | Sí |
| Estandarizado | Sí, estructura fija | Sí, códigos; el cuerpo es libre (o RFC 9457) |
| Detalle de negocio | En Detail |
En el cuerpo JSON |
- Tabla comparativa completa
| Criterio | SOAP | REST |
|---|---|---|
| Naturaleza | Protocolo con especificación formal | Estilo arquitectónico |
| Formato | Solo XML | Cualquiera; en la práctica JSON |
| Contrato | WSDL obligatorio y formal | OpenAPI opcional |
| Transporte | Agnóstico: HTTP, SMTP, JMS, TCP | HTTP exclusivamente |
| Verbos | Solo POST (cuando va sobre HTTP) |
GET, POST, PUT, PATCH, DELETE |
| Direccionamiento | Un endpoint por servicio | Una URI por recurso |
| Estado | Puede ser con o sin estado | Sin estado por definición |
| Caché | No aprovecha la de HTTP | Nativa del protocolo |
| Seguridad | WS-Security (nivel de mensaje) + TLS | TLS + OAuth 2.0 / JWT (nivel de canal) |
| Transacciones | WS-AtomicTransaction | No estándar; sagas y compensación |
| Errores | SOAP Fault en el cuerpo | Códigos de estado + cuerpo |
| Tamaño del mensaje | Grande (sobre, espacios de nombres, XML) | Reducido |
| Rendimiento | Menor: parseo XML y verbosidad | Mayor |
| Curva de aprendizaje | Pronunciada | Suave |
| Tooling | Excelente en Java y .NET; escaso fuera | Universal; basta un navegador o curl |
| Generación de clientes | Automática desde WSDL | Automática desde OpenAPI, si existe |
| Consumo desde navegador | Muy incómodo | Natural |
| Adopción actual | Sistemas heredados y sectores regulados | Estándar de facto en la web |
- Cuándo sigue teniendo sentido SOAP
Sería un error caricaturizar SOAP como "lo antiguo y malo". Hay contextos donde sus propiedades siguen siendo las adecuadas:
- Banca y sistemas de pago interbancarios. Muchos protocolos financieros exigen firma digital del mensaje con validez probatoria, y a menudo el estándar del sector ya está definido en SOAP.
- Seguros. Los intercambios entre aseguradoras y con organismos reguladores están estandarizados en esquemas XML consolidados hace veinte años.
- Sanidad. HL7 y otros estándares clínicos tienen larga tradición XML, con requisitos estrictos de estructura y validación.
- Administración pública. Muchos servicios de facturación electrónica, notificaciones y firma están definidos como servicios SOAP.
- Sistemas heredados. Un ERP con quince años de vida expone SOAP, y reescribirlo no es una opción realista.
- Requisitos de transaccionalidad distribuida. Cuando de verdad se necesita "todo o nada" entre varios servicios con garantías fuertes.
- Contratos que deben ser vinculantes y verificables entre organizaciones, con validación estricta y no repudio.
El denominador común: entornos regulados, con contratos entre organizaciones, requisitos legales de firma y sistemas de larga vida.
- Cuándo elegir REST
Para todo lo demás, y desde luego para un proyecto nuevo orientado a la web:
- APIs públicas que quieres que la gente adopte sin fricción.
- Aplicaciones web y móviles, donde el peso del mensaje y la simplicidad del cliente importan.
- Contenido cacheable, como el catálogo de Tienda Aroma.
- Ecosistemas de terceros: cuanto más fácil sea empezar, más integraciones tendrás.
- Equipos heterogéneos con distintos lenguajes y sin tooling empresarial uniforme.
- Iteración rápida, donde generar y regenerar contratos formales frenaría el desarrollo.
Para Tienda Aroma la decisión es evidente: su API la consumen una web, una app móvil, un panel interno y socios externos. No hay firma digital con valor legal, ni transacciones distribuidas entre organizaciones, y sí una necesidad clara de caché y de adopción fácil. REST, sin dudarlo.
- Fachadas REST sobre servicios SOAP heredados
Un patrón que encontrarás con enorme frecuencia en empresas medianas y grandes: no se puede tirar el sistema SOAP heredado, pero los clientes modernos (móvil, web, socios) no quieren tocar XML. La solución es una fachada REST que traduce.
graph LR
A["Aroma Móvil"] -->|"GET /v1/facturas/fac_88<br/>JSON"| F["Fachada REST<br/>(Node.js / gateway)"]
W["Web"] -->|JSON| F
F -->|"SOAP + XML<br/>obtenerFactura"| L["Sistema de facturación<br/>heredado (SOAP)"]
F -->|"SOAP + XML"| C["ERP contable<br/>(SOAP)"]
Supongamos que la facturación de Tienda Aroma la lleva un ERP antiguo que solo habla SOAP. La fachada:
- Recibe
GET /v1/pedidos/ped_5001/facturacon un token moderno. - Valida permisos y traduce la petición a un sobre SOAP con las credenciales que el ERP espera.
- Recibe el XML, extrae los datos y los convierte en JSON limpio con nombres coherentes con el resto de la API.
- Traduce los
Faulten códigos de estado HTTP adecuados (404,403,503). - Añade caché donde tenga sentido, aliviando la carga sobre un sistema antiguo y frágil.
Ventajas: los clientes nuevos ven una API homogénea, el sistema heredado no se toca y se puede sustituir por detrás sin que nadie se entere. Inconvenientes: un salto más de latencia, una pieza más que mantener y el riesgo de que la fachada acabe filtrando conceptos raros del sistema antiguo (códigos crípticos, campos con nombres incomprensibles) si no se cuida el diseño.
Este trabajo de traducción y homogeneización es exactamente una de las funciones de un API gateway, que veremos en la lección 05-06.
Errores Comunes y Consejos
- Decir "una API SOAP RESTful". Son cosas de categorías distintas: SOAP es un protocolo, REST un estilo. Un servicio SOAP está, por construcción, en el nivel 0 de Richardson.
- Despreciar SOAP por antiguo. Si te toca integrarte con banca o administración, encontrarás soluciones sólidas que llevan décadas funcionando. La actitud profesional es entender por qué son así.
- Creer que TLS equivale a WS-Security. TLS protege el canal punto a punto; WS-Security protege el mensaje de extremo a extremo, atravesando intermediarios. Son garantías distintas.
- Reproducir SOAP con JSON. Un endpoint único que recibe
{"operacion": "..."}es SOAP sin sus ventajas: tienes la rigidez del nivel 0 y ninguna de sus garantías formales. - Olvidar el
Content-Typecorrecto al llamar a un servicio SOAP. SOAP 1.1 esperatext/xml; SOAP 1.2,application/soap+xml. Confundirlos produce errores desconcertantes. - Diseñar una fachada REST como calco del servicio SOAP. Si tu API expone
POST /v1/obtenerFacturaRequest, has trasladado el problema en vez de resolverlo. - Consejo: si trabajas con un servicio SOAP, pide siempre el WSDL primero. Con él, herramientas como SoapUI o los generadores de tu lenguaje te dan un cliente funcional en minutos.
Ejercicios
Ejercicio 1: analizar un mensaje SOAP
Dado este mensaje, responde: (a) ¿qué operación invoca?; (b) ¿dónde viajan las credenciales y por qué ahí y no en una cabecera HTTP?; (c) ¿podría cachearse la respuesta?; (d) ¿cuál sería el equivalente REST de esta operación?
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:tien="http://tiendaaroma.example/servicios">
<soap:Header>
<tien:Credenciales><tien:token>abc123</tien:token></tien:Credenciales>
</soap:Header>
<soap:Body>
<tien:ListarPedidosClientePeticion>
<tien:clienteId>cli_842</tien:clienteId>
<tien:desde>2026-01-01</tien:desde>
</tien:ListarPedidosClientePeticion>
</soap:Body>
</soap:Envelope>Ejercicio 2: elegir tecnología con criterio
Para cada escenario, decide SOAP o REST y justifica con dos argumentos concretos:
- Tienda Aroma quiere que blogs de café muestren su catálogo.
- Un banco debe enviar órdenes de transferencia firmadas digitalmente a otro banco, con no repudio y validez legal.
- Aroma Móvil necesita cargar el catálogo rápido en redes móviles lentas.
- Una aseguradora debe intercambiar partes de siniestro con un consorcio que ya tiene definido un esquema XML estándar.
- Un servicio interno de inventario debe actualizar stock en tres sistemas y garantizar que, si uno falla, ninguno queda modificado.
Ejercicio 3: diseñar una fachada REST
El ERP heredado de Tienda Aroma expone estas tres operaciones SOAP. Diseña la fachada REST equivalente indicando método, ruta, código de estado en caso de éxito y qué error HTTP devolverías en el caso indicado.
| Operación SOAP | Descripción | Caso de error |
|---|---|---|
obtenerFacturaPorPedido(pedidoId) |
Devuelve la factura de un pedido | El pedido no tiene factura todavía |
anularFactura(facturaId, motivo) |
Anula una factura emitida | La factura ya está anulada |
listarFacturasCliente(clienteId, anio) |
Facturas de un cliente en un año | El cliente no existe |
Soluciones
Solución 1
- (a) Invoca
ListarPedidosCliente, pidiendo los pedidos del clientecli_842desde el 1 de enero de 2026. - (b) Las credenciales viajan en el
Headerdel sobre SOAP. La razón de diseño es la independencia del transporte: si el mensaje viaja por SMTP o por una cola JMS en lugar de HTTP, no hay cabeceras HTTP donde ponerlas. Además, así pueden ir firmadas junto al mensaje y sobrevivir a los intermediarios. - (c) No. Es un
POSTa un endpoint único, y ni las cachés HTTP ni los proxys pueden saber que en realidad es una lectura. Se pierde por completo el mecanismo de caché del protocolo. - (d)
GET /v1/pedidos?clienteId=cli_842&desde=2026-01-01, conAuthorization: Bearer abc123y respuesta200 OK. Alternativa igualmente válida:GET /v1/clientes/cli_842/pedidos?desde=2026-01-01, que expresa la relación en la ruta.
Solución 2
- REST. La adopción por terceros debe ser inmediata (basta
curlofetch), y el catálogo es contenido cacheable, con lo que se reduce carga y latencia. - SOAP. Se necesita firma a nivel de mensaje con no repudio (WS-Security), que TLS no proporciona, y lo más probable es que el estándar interbancario del sector ya esté definido en SOAP.
- REST. Los mensajes JSON pesan una fracción de un sobre SOAP, y las respuestas del catálogo se pueden cachear en el dispositivo y en una CDN.
- SOAP. El esquema XML ya existe y es vinculante entre las partes; el WSDL genera clientes validados automáticamente y la validación estricta es un requisito, no una comodidad.
- Depende, y es el caso más matizado. Si se exige atomicidad estricta entre sistemas heterogéneos, WS-AtomicTransaction la ofrece de forma estándar. En una arquitectura moderna, sin embargo, lo habitual es resolverlo con REST o mensajería más un patrón saga con operaciones de compensación, aceptando consistencia eventual a cambio de mucha menos complejidad operativa. Lo importante es que la decisión sea explícita.
Solución 3
GET /v1/pedidos/ped_5001/factura -> 200 OK | error: 404 Not Found POST /v1/facturas/fac_88/anulacion -> 201 Created (o 200 OK) | error: 409 Conflict GET /v1/clientes/cli_842/facturas?anio=2026 -> 200 OK | error: 404 Not Found
Justificación:
- Obtener factura: es una lectura, luego
GET. La factura se modela como subrecurso del pedido, lo que refleja la relación. Si aún no existe,404 Not Found, porque el recurso solicitado no está. - Anular factura: no se usa
DELETE, porque anular no es borrar: la factura sigue existiendo con estado anulado, y en contabilidad eso es obligatorio. Se modela la anulación como un subrecurso al que se hacePOST, enviando el motivo en el cuerpo. Si ya estaba anulada,409 Conflict, que expresa exactamente "el estado actual del recurso impide esta operación". (Una alternativa defendible esPATCH /v1/facturas/fac_88con{"estado":"anulada"}; el subrecurso resulta más expresivo cuando la acción exige datos propios como el motivo.) - Listar facturas de un cliente:
GETsobre la subcolección, con el año como filtro en la query string, ya que modula la consulta y no identifica el recurso. Si el cliente no existe,404; si existe pero no tiene facturas ese año,200 OKcon una lista vacía, no404: la colección existe, simplemente está vacía.
Conclusión
SOAP y REST responden a dos filosofías distintas: SOAP es un protocolo formal, transporte-agnóstico, con contrato WSDL y una pila de extensiones para seguridad, fiabilidad y transacciones; REST es un estilo que se apoya en HTTP y aprovecha sus recursos, verbos, códigos y caché. Hemos visto la misma consulta de un café en ambos, comprobando que SOAP multiplica por cuatro el tamaño del mensaje, renuncia a la caché y esconde el resultado dentro del cuerpo, mientras que REST convierte al café en un recurso direccionable, cacheable y probable desde el navegador. También hemos reconocido lo que SOAP hace mejor —firma de mensaje extremo a extremo, validación estricta y transacciones distribuidas— y por qué sigue vivo en banca, seguros, sanidad y administración, así como el patrón habitual de envolver esos servicios con una fachada REST.
Queda una última pieza del mapa. En la siguiente lección, REST frente a GraphQL, gRPC y webhooks, veremos las alternativas contemporáneas: qué problemas concretos de REST resuelve cada una, qué problemas nuevos introducen y por qué lo normal hoy no es elegir una sola, sino combinarlas. Con ello cerraremos el módulo y estaremos listos para diseñar la API de Tienda Aroma en el módulo 2.
Curso de REST API: Principios de Diseño y Desarrollo de APIs RESTful
Módulo 1: Introducción a las APIs RESTful
- ¿Qué es una API?
- Historia y evolución de las APIs
- Fundamentos de HTTP para APIs
- Principios básicos de REST
- Modelo de madurez de Richardson y HATEOAS
- REST vs. SOAP
- REST frente a GraphQL, gRPC y webhooks
Módulo 2: Diseño de APIs RESTful
- Principios de diseño de APIs RESTful
- Recursos y URIs
- Métodos HTTP
- Códigos de estado HTTP
- Representaciones, cabeceras y negociación de contenido
- Filtrado, ordenación, paginación y búsqueda
- Versionado de APIs
- Documentación de APIs
Módulo 3: Desarrollo de APIs RESTful
- Configuración del entorno de desarrollo
- Creación de un servidor básico
- Manejo de peticiones y respuestas
- Validación de datos de entrada
- Persistencia y capa de acceso a datos
- Autenticación y autorización
- Manejo de errores
- Pruebas y validación
Módulo 4: Buenas Prácticas y Seguridad
- Buenas prácticas en el diseño de APIs
- Seguridad en APIs RESTful
- OAuth 2.0 y OpenID Connect en la práctica
- Rate limiting y throttling
- CORS y políticas de seguridad
- Caché HTTP y rendimiento
- Observabilidad: logs, métricas y trazas
Módulo 5: Herramientas y Frameworks
- Postman para pruebas de APIs
- Swagger y OpenAPI para documentación
- Frameworks populares para APIs RESTful
- Contratos, mocks y pruebas automatizadas de API
- Integración continua y despliegue
- API gateways y portales de desarrollador
