Una API define qué puede pedir un cliente, cómo expresa su intención y qué significa la respuesta. Su diseño conecta la experiencia del consumidor con las reglas del sistema: identidad, acceso a recursos, consistencia y recuperación cuando una petición falla.

El contrato debe poder explicarse antes de elegir un protocolo. Crear una reserva, consultar eventos y modificar un evento son operaciones distintas; cada una necesita entradas, resultados y condiciones de error claras.

Empezar por el contrato

Conviene identificar a los consumidores y sus operaciones principales. Un navegador puede necesitar listas pequeñas y respuestas fáciles de inspeccionar; un servicio interno puede requerir llamadas tipadas o un flujo continuo de mensajes. También importan frecuencia, tamaño, latencia y evolución independiente de clientes y servidores.

Para cada operación se define qué recurso afecta, quién puede ejecutarla, qué campos acepta y qué resultado confirma. Una respuesta exitosa al recibir trabajo no equivale necesariamente a terminarlo. Del mismo modo, un timeout no demuestra que la operación nunca ocurrió.

Las invariantes del negocio completan el contrato. Una reserva debe pertenecer al usuario autenticado, cubrir inventario disponible y tener una expiración definida por el servidor. El método HTTP ayuda a comunicar la intención, pero esas reglas se protegen en la aplicación y en su almacenamiento.

Estilos de interfaz

Una API HTTP orientada a recursos suele ser un punto de partida práctico. Permite aprovechar métodos, códigos de estado, herramientas y mecanismos de caché conocidos. La elección sigue dependiendo del contrato y de los consumidores.

EnfoqueCuándo puede ayudarCostes que deben contemplarse
REST sobre HTTPRecursos y operaciones que encajan con rutas y representaciones claras.Granularidad de respuestas, evolución y coordinación entre varias llamadas.
GraphQLConsumidores que necesitan seleccionar campos y combinar datos relacionados.Coste de consultas, resolvers, autorización por objeto y campo, y estrategia de caché.
gRPCRPC tipado entre servicios, contratos compartidos y necesidades de streaming.Compatibilidad de clientes e intermediarios, evolución del esquema y herramientas operativas.

GraphQL puede reducir over-fetching y under-fetching: devolver campos innecesarios o exigir llamadas adicionales para completar una vista. Eso no garantiza una consulta barata. Profundidad, cantidad de objetos y trabajo de los resolvers requieren límites y medición; la autorización debe seguir protegiendo los datos seleccionados. La caché también necesita una estrategia compatible con las consultas y la identidad del consumidor.

gRPC suele combinar mensajes definidos con Protobuf y transporte HTTP/2. Su representación binaria puede reducir tamaño y coste de serialización frente a ciertas alternativas; el rendimiento depende de los payloads, clientes y carga medida. Puede encajar en comunicación interna, pero no es un requisito de todo microservicio. El soporte del cliente y del entorno importa; un navegador puede necesitar una integración o gateway apropiados. Ninguna de estas opciones elimina el diseño de errores, permisos o reintentos.

RPC expresa operaciones como llamadas a procedimientos remotos: un nombre, parámetros y un resultado. gRPC es una forma de RPC; la especificación JSON-RPC 2.0 define un formato JSON de mensajes, respuestas y errores, sin imponer un único transporte ni los métodos de la aplicación. Los métodos y sus garantías pertenecen al contrato concreto, no al formato por sí solo. El id correlaciona la respuesta con la petición; no funciona como una clave de idempotencia.

En Ethereum, wallets y dApps pueden usar JSON-RPC como puente hacia un nodo accesible, local o remoto, operado directamente o por un proveedor. Así pueden acceder a su API sin mantener un nodo completo propio. eth_getBalance consulta un balance respecto a un bloque; eth_call ejecuta una llamada sin publicar una transacción y no garantiza su éxito posterior; eth_sendRawTransaction envía una transacción firmada cuyo hash no confirma inclusión ni finalidad. Una llamada remota añade latencia, timeouts y errores: tras un envío de resultado incierto hay que recuperar su estado, no asumir que nunca llegó. La vista del nodo y la referencia de bloque determinan qué estado se consulta. Este acceso abstrae la interfaz del nodo, no el consenso ni la confianza en sus respuestas; privacidad, confianza y verificación dependen de la conexión y del caso. El ejemplo no convierte JSON-RPC ni esos métodos en una regla para todas las cadenas. Sus entradas y resultados se describen en la referencia JSON-RPC de Ethereum.

Recursos y ubicación de las entradas

Las rutas suelen usar nombres de recursos en plural: /events, /holds y /orders. Un identificador en el path selecciona un recurso concreto; los parámetros de query describen filtros o la forma de recuperar una colección.

UbicaciónEjemploResponsabilidad
Path/events/{eventId}Identificar el recurso sobre el que se opera.
Query/events?city=CDMX&limit=20Filtrar, ordenar y paginar resultados.
HeadersAuthorization, Idempotency-Key, If-MatchTransportar credenciales o condiciones de la petición.
BodyeventId y seatIds al solicitar una reservaExpresar los datos o la acción aceptados por el contrato.

El contrato define tipos, campos obligatorios, límites y qué ocurre con campos desconocidos. También distingue validación sintáctica de reglas del negocio: una lista de asientos puede ser JSON válido y aun así contener inventario ocupado o fuera del evento indicado.

---
config:
  look: handDrawn
---
flowchart TD
    client["Cliente"] --> request["Petición<br/>Método, ruta y entradas"]
    request --> auth["Autenticación cuando aplica<br/>Autorización"]
    auth --> contract["Validar contrato<br/>y condiciones"]
    contract --> service["Aplicación<br/>Reglas del negocio"]
    service --> store[("Estado autoritativo")]
    service --> response["Resultado o error<br/>Contrato de respuesta"]
    response --> client

El diagrama resume responsabilidades, no un orden rígido de ejecución: algunas comprobaciones de autorización y condiciones necesitan leer el recurso y coordinarse con su actualización.

Métodos HTTP: POST y PUT no son equivalentes

El método describe la semántica de la petición. La idempotencia significa que repetir la misma petición tiene el mismo efecto previsto que ejecutarla una vez; no exige devolver siempre el mismo código ni el mismo cuerpo.

MétodoIntención habitualIdempotencia del método
GETRecuperar una representación sin solicitar una modificación del recurso.Sí; es seguro, aunque pueda generar logs o métricas.
POSTPedir al recurso que procese la representación enviada; puede crear un recurso o ejecutar una operación.No la garantiza; el contrato puede añadirla con una clave.
PUTCrear o reemplazar la representación del recurso en una URI conocida.Sí para el efecto previsto de repetir la misma petición.
PATCHAplicar una modificación parcial.No la garantiza; depende de la operación definida.
DELETEEliminar la asociación del recurso con su URI.Sí; una repetición puede responder que ya no existe.

En POST /holds, el servidor puede asignar el identificador de una nueva reserva. El cliente envía la selección y una clave para recuperar el mismo intento ante un reintento:

POST /holds HTTP/1.1
Authorization: Bearer <access-token>
Content-Type: application/json
Idempotency-Key: hold-attempt-123
 
{"eventId":"event-123","seatIds":["seat-A1"]}

Una creación completada puede responder 201 Created, con Location: /holds/hold-456 y una representación que incluya holdId, estado y expiración. El servidor deriva al propietario de la identidad autenticada y valida la selección.

En cambio, un organizador autorizado puede usar PUT /events/event-123 para enviar la representación completa de los campos modificables del evento conocido. El contrato establece qué significa omitir un campo y qué campos gestiona el servidor. Si PUT crea un recurso puede responder 201; si reemplaza uno existente, 200 o 204 según el contrato.

Repetir ese PUT no debe crear otro evento. Sin embargo, idempotencia no evita sobrescribir el cambio concurrente de otro cliente. Una versión o un ETag con If-Match, comprobados junto con la escritura, permiten rechazar una actualización basada en una versión anterior con 412 Precondition Failed.

PATCH exige la misma precisión: asignar un valor puede ser idempotente, mientras que incrementar un contador o añadir un elemento puede repetir el efecto. El nombre del método no convierte esas operaciones en seguras para reintentar.

Paginación y orden

Una colección debe limitar su tamaño y documentar un orden. Para eventos ordenados por fecha, el par (starts_at, id) proporciona un desempate estable. El cursor representa la posición y debe conservar el contexto de filtros y orden; puede ser opaco para el consumidor.

Una petición como GET /events?limit=20&after=<cursor> recupera la siguiente página según ese contrato. El tamaño máximo evita respuestas arbitrariamente grandes. Un cursor opaco tampoco es una credencial: el servidor valida sus entradas y los permisos de cada consulta.

Cursor y offset ofrecen compromisos diferentes. Offset es sencillo para listas pequeñas o saltos de página, pero puede requerir más trabajo al avanzar y desplazar resultados cuando cambian los datos. El cursor puede aprovechar un acceso alineado con el orden; el modelo de datos y sus índices deben sostener esa consulta.

El desempate no crea un snapshot. Inserciones, borrados o cambios del campo de orden pueden alterar lo que aparece entre páginas. Si se requiere una vista fija, el contrato necesita una versión o estrategia de snapshot explícita. El conteo total también tiene un coste y no tiene que formar parte de toda respuesta.

Autenticación y autorización

La autenticación identifica al consumidor; la autorización determina qué puede hacer sobre el recurso. El catálogo puede permitir GET /events sin sesión, mientras que POST /holds exige identidad y permisos y el PUT de un evento requiere autorización del organizador. Un JWT requiere validar firma, algoritmos admitidos, emisor, audiencia y expiración según el contrato. Leer sus claims sin verificarlos no establece una identidad confiable.

Una sesión con cookie también es válida. Necesita HTTPS, atributos de cookie apropiados como Secure y HttpOnly, y medidas frente a CSRF para las operaciones que cambian estado. El mecanismo debe encajar con el cliente y su modelo de amenazas.

Un orderId o userId enviado por el cliente puede identificar el objetivo de una operación, siempre que el servidor autorice ese acceso. No debe aceptarse un ownerId del body como prueba de identidad. La autorización contempla objeto, tenant, rol y campos modificables; también protege operaciones de colección y resolvers de GraphQL.

La validación limita tamaños, tipos y campos permitidos, y evita asignar directamente todos los campos recibidos a un modelo interno. Las credenciales no deben ir en la query. Autorización y validación se complementan con límites de tráfico que protejan capacidad y controlen abuso.

Respuestas, reintentos y evolución

Los errores deben ofrecer un contrato estable: código de aplicación, mensaje útil y un identificador de petición para diagnóstico, sin detalles internos sensibles. Códigos como 400, 401, 403, 404, 409 y 429 distinguen entradas inválidas, autenticación, permisos, ausencia, conflictos y límites; la política de exposición puede evitar revelar la existencia de un recurso privado.

Una clave de idempotencia se vincula al usuario, operación y payload aceptado, con una política de retención definida. Reutilizarla con datos incompatibles debe producir un conflicto. Registrar la decisión y coordinar peticiones concurrentes impide ejecutar dos veces el mismo intento dentro de ese contrato. Si una dependencia externa participa, sus garantías también importan: el caso de boletaje muestra cómo recuperar compras y pagos de resultado incierto.

Los reintentos deben ser acotados y respetar backoff y señales como Retry-After. Un timeout exige consultar el estado o reintentar idempotentemente; cambiar de clave puede crear otra operación. Si el trabajo se acepta de forma asíncrona, 202 Accepted puede incluir una URL para consultar su estado, pero no confirma su finalización. Las colas de trabajo completan ese contrato con entrega y recuperación explícitas.

La evolución protege a los consumidores existentes: cambios de campos, validación y semántica requieren una política de compatibilidad y pruebas sobre contratos reales. Diseñar una API consiste en hacer esas decisiones observables y sostenibles, junto con los modelos y mecanismos del recorrido de System Design.