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

  1. Qué es SOAP exactamente
  2. La estructura del sobre: Envelope, Header y Body
  3. El contrato: WSDL
  4. La pila WS-*
  5. El mismo caso lado a lado: consultar un café
  6. Manejo de errores: SOAP Fault frente a códigos HTTP
  7. Tabla comparativa completa
  8. Cuándo sigue teniendo sentido SOAP
  9. Cuándo elegir REST
  10. Fachadas REST sobre servicios SOAP heredados

  1. 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 obtenerCafe o crearPedido, 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.

  1. 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 Body que 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.

  1. 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).

  1. 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:

  1. 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.
  2. 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.

  1. 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 POST aunque 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 a caf_001, así que no se puede enlazar ni marcar.
  • La cabecera SOAPAction indica 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 OK incluso 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_001

En 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 curl en diez segundos, sin generar código ni instalar nada.

  1. 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
Estandarizado Sí, estructura fija Sí, códigos; el cuerpo es libre (o RFC 9457)
Detalle de negocio En Detail En el cuerpo JSON

  1. 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

  1. 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.

  1. 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.

  1. 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:

  1. Recibe GET /v1/pedidos/ped_5001/factura con un token moderno.
  2. Valida permisos y traduce la petición a un sobre SOAP con las credenciales que el ERP espera.
  3. Recibe el XML, extrae los datos y los convierte en JSON limpio con nombres coherentes con el resto de la API.
  4. Traduce los Fault en códigos de estado HTTP adecuados (404, 403, 503).
  5. 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-Type correcto al llamar a un servicio SOAP. SOAP 1.1 espera text/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:

  1. Tienda Aroma quiere que blogs de café muestren su catálogo.
  2. Un banco debe enviar órdenes de transferencia firmadas digitalmente a otro banco, con no repudio y validez legal.
  3. Aroma Móvil necesita cargar el catálogo rápido en redes móviles lentas.
  4. Una aseguradora debe intercambiar partes de siniestro con un consorcio que ya tiene definido un esquema XML estándar.
  5. 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 cliente cli_842 desde el 1 de enero de 2026.
  • (b) Las credenciales viajan en el Header del 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 POST a 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, con Authorization: Bearer abc123 y respuesta 200 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

  1. REST. La adopción por terceros debe ser inmediata (basta curl o fetch), y el catálogo es contenido cacheable, con lo que se reduce carga y latencia.
  2. 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.
  3. 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.
  4. 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.
  5. 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 hace POST, 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 es PATCH /v1/facturas/fac_88 con {"estado":"anulada"}; el subrecurso resulta más expresivo cuando la acción exige datos propios como el motivo.)
  • Listar facturas de un cliente: GET sobre 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 OK con una lista vacía, no 404: 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

Módulo 2: Diseño de APIs RESTful

Módulo 3: Desarrollo de APIs RESTful

Módulo 4: Buenas Prácticas y Seguridad

Módulo 5: Herramientas y Frameworks

Módulo 6: Casos de Estudio y Proyectos

© Copyright 2026. Todos los derechos reservados