Introducción Completa a la Especificación JSON:API

Rebecca Kovács

Rebecca Kovács

19 May 2025

Introducción Completa a la Especificación JSON:API

REST (Representational State Transfer) proporciona un estilo arquitectónico fundamental para construir servicios web. Sin embargo, deja sin definir muchos aspectos del formato de solicitud y respuesta. Esta ambigüedad puede generar inconsistencias, aumentar la sobrecarga de desarrollo y una curva de aprendizaje más pronunciada para los consumidores de API. Aquí entra JSON:API, una especificación que proporciona un enfoque estandarizado y basado en convenciones para construir APIs en JSON.

Esta guía completa ofrece una inmersión profunda en la especificación JSON:API, explorando sus conceptos centrales, estructura y potentes características. Diseccionaremos sus mecanismos para obtener, crear, actualizar y eliminar recursos, gestionar relaciones, manejar errores y optimizar la transferencia de datos, equipándote con el conocimiento para diseñar y consumir APIs robustas y eficientes.

💡
¿Quieres una excelente herramienta de prueba de API que genere hermosa documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu equipo de desarrolladores trabaje en conjunto con máxima productividad?

Apidog cumple todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
button

¿Por qué JSON:API? Explicación:

Antes de profundizar en las complejidades técnicas, es crucial comprender los problemas que JSON:API busca resolver. Sin una convención compartida, los desarrolladores de API a menudo dedican un tiempo considerable a debatir:

JSON:API aborda esto definiendo un formato claro y consistente para solicitudes y respuestas. Esta estandarización ofrece varios beneficios clave:

Conceptos centrales: Los bloques de construcción de un documento JSON:API

En su esencia, JSON:API gira en torno al concepto de recursos. Un recurso es un registro individual de un tipo particular, como un "artículo", un "usuario" o un "producto". Cada documento JSON:API, ya sea una solicitud o una respuesta, se adhiere a una estructura específica.

La estructura del documento: Miembros de nivel superior

Un documento JSON:API es un objeto JSON que debe contener al menos uno de los siguientes miembros de nivel superior:

Además, un documento puede contener estos miembros de nivel superior:

Objetos de recurso: Representando tus datos

Un objeto de recurso es la piedra angular de JSON:API y debe contener:

Un objeto de recurso puede también contener:

Ejemplo de un objeto de recurso:JSON

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "JSON:API Revelado",
    "body": "Una inmersión profunda en la especificación...",
    "created_at": "2025-05-15T10:00:00Z",
    "updated_at": "2025-05-16T14:30:00Z"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "/articles/1/relationships/author",
        "related": "/articles/1/author"
      },
      "data": { "type": "users", "id": "42" }
    },
    "comments": {
      "links": {
        "self": "/articles/1/relationships/comments",
        "related": "/articles/1/comments"
      },
      "data": [
        { "type": "comments", "id": "5" },
        { "type": "comments", "id": "12" }
      ]
    }
  },
  "links": {
    "self": "/articles/1"
  }
}

Objetos identificadores de recurso

Los objetos identificadores de recurso son representaciones mínimas de un recurso, que contienen solo type e id. Se utilizan dentro de los objetos de relación para enlazar a otros recursos sin incrustar el objeto de recurso completo.

Ejemplo de un objeto identificador de recurso:JSON

{ "type": "users", "id": "42" }

Los objetos links proporcionan URLs para navegar por la API. Los miembros de enlace comunes incluyen:

Un enlace puede representarse como:

Ejemplo de un objeto Links (dentro de una relación):JSON

"links": {
  "self": "http://example.com/articles/1/relationships/author",
  "related": "http://example.com/articles/1/author"
}

Objetos Meta

Los objetos meta permiten la inclusión de meta-información no estándar. Pueden ser pares clave-valor arbitrarios. Por ejemplo, un objeto meta podría incluir información de derechos de autor o marcas de tiempo relacionadas con los datos.

Ejemplo de un objeto Meta:JSON

"meta": {
  "copyright": "Copyright 2025 Example Corp.",
  "authors": ["John Doe"]
}

Negociación de contenido: Hablando el lenguaje correcto

JSON:API define su propio tipo de medio: application/vnd.api+json.

Los servidores pueden admitir otros tipos de medio junto con application/vnd.api+json a través de la negociación de contenido estándar.

Obtención de datos: Recuperando recursos y colecciones

JSON:API proporciona mecanismos robustos para que los clientes recuperen datos precisamente según sea necesario.

Obteniendo recursos individuales

Para obtener un solo recurso, un cliente envía una solicitud GET a un endpoint que representa ese recurso.

Solicitud:

GET /articles/1

Accept: application/vnd.api+json

Respuesta exitosa (200 OK):JSON

{
  "links": {
    "self": "/articles/1"
  },
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "¡JSON:API Mola!"
    }
    // ... otros atributos y relaciones
  }
}

Si el recurso no existe, el servidor debería devolver un 404 Not Found. Si se obtiene un enlace de recurso relacionado uno a uno y la relación está vacía, los datos primarios serán null.

Obteniendo colecciones de recursos

Para obtener una colección de recursos, un cliente envía una solicitud GET a un endpoint que representa esa colección.

Solicitud:

GET /articles

Accept: application/vnd.api+json

Respuesta exitosa (200 OK):JSON

{
  "links": {
    "self": "/articles",
    "next": "/articles?page[offset]=10",
    "last": "/articles?page[offset]=50"
  },
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": { "title": "Artículo 1" }
      // ...
    },
    {
      "type": "articles",
      "id": "2",
      "attributes": { "title": "Artículo 2" }
      // ...
    }
    // ... más artículos
  ]
}

Si la colección está vacía, el miembro data será un array vacío [].

Relaciones: Conectando recursos

Las relaciones son una parte fundamental de la mayoría de los modelos de datos. JSON:API proporciona una forma clara de definirlas e interactuar con ellas.

Representando relaciones

Las relaciones se definen dentro del objeto relationships de un recurso. Cada entrada en el objeto relationships representa una relación distinta (por ejemplo, "author", "comments").

Un objeto de relación debe contener al menos uno de:

Ejemplo de relaciones "author" (uno a uno) y "comments" (uno a muchos):JSON

"relationships": {
  "author": {
    "links": {
      "self": "/articles/1/relationships/author",
      "related": "/articles/1/author"
    },
    "data": { "type": "users", "id": "42" }
  },
  "comments": {
    "links": {
      "self": "/articles/1/relationships/comments",
      "related": "/articles/1/comments"
    },
    "data": [
      { "type": "comments", "id": "5" },
      { "type": "comments", "id": "12" }
    ]
  }
}

Obteniendo relaciones

Los clientes pueden obtener información sobre una relación en sí o sobre los recursos relacionados utilizando los enlaces proporcionados.

Obteniendo la vinculación de la relación (enlace self):

GET /articles/1/relationships/comments

Accept: application/vnd.api+json

Esto devuelve una colección de objetos identificadores de recurso para los comentarios relacionados con el artículo "1".

Obteniendo recursos relacionados (enlace related):

GET /articles/1/comments

Accept: application/vnd.api+json

Esto devuelve una colección de objetos de recurso de comentario completos relacionados con el artículo "1".

Optimizando la recuperación de datos

JSON:API ofrece varias características para optimizar la forma en que se recuperan los datos, minimizando el ancho de banda y mejorando el rendimiento del lado del cliente.

Documentos compuestos: Reduciendo solicitudes HTTP con include

Para evitar múltiples viajes de ida y vuelta al servidor para obtener recursos relacionados, JSON:API permite a los clientes solicitar que los recursos relacionados se incluyan en la respuesta primaria utilizando el parámetro de consulta include. El servidor cargará lateralmente estos recursos en el array included de nivel superior.

Solicitud para obtener un artículo e incluir su autor y comentarios:

GET /articles/1?include=author,comments

Accept: application/vnd.api+json

Respuesta (200 OK):JSON

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": { "title": "..." },
    "relationships": {
      "author": {
        "data": { "type": "users", "id": "42" }
      },
      "comments": {
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    }
  },
  "included": [
    {
      "type": "users",
      "id": "42",
      "attributes": { "name": "John Doe" }
    },
    {
      "type": "comments",
      "id": "5",
      "attributes": { "body": "¡Excelente artículo!" }
    },
    {
      "type": "comments",
      "id": "12",
      "attributes": { "body": "Muy informativo." }
    }
  ]
}

Conjuntos de campos dispersos (Sparse Fieldsets): Obteniendo solo los campos necesarios

Los clientes pueden solicitar que solo se devuelvan campos específicos (atributos y relaciones) para recursos de un tipo dado utilizando el parámetro de consulta fields[TYPE]. Esto reduce el tamaño de la carga útil.

Solicitud para obtener artículos, pero solo su título y la relación con el autor:

GET /articles?fields[articles]=title,author

Accept: application/vnd.api+json

Respuesta (200 OK):JSON

{
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "Artículo 1"
      },
      "relationships": {
        "author": {
          "data": { "type": "users", "id": "42" }
        }
      }
    }
    // ... otros artículos con solo título y autor
  ]
}

Ordenación (Sorting)

Los clientes pueden solicitar que los datos primarios se ordenen utilizando el parámetro de consulta sort.

Solicitud para obtener artículos ordenados por fecha de creación (descendente) y luego por título (ascendente):

GET /articles?sort=-created_at,title

Accept: application/vnd.api+json

Paginación

JSON:API admite varias estrategias de paginación. La especificación define cómo deben aparecer los enlaces de paginación (first, prev, next, last) en el objeto links de nivel superior. La estrategia de paginación real (por ejemplo, basada en página, basada en offset, basada en cursor) la determina el servidor utilizando parámetros de consulta como page[number], page[size], page[offset], page[limit] o page[cursor].

Ejemplo de enlaces de paginación basados en página:JSON

"links": {
  "self": "/articles?page[number]=2&page[size]=10",
  "first": "/articles?page[number]=1&page[size]=10",
  "prev": "/articles?page[number]=1&page[size]=10",
  "next": "/articles?page[number]=3&page[size]=10",
  "last": "/articles?page[number]=5&page[size]=10"
}

Los clientes deben usar estos enlaces proporcionados en lugar de construir sus propias URLs de paginación.

Filtrado (Filtering)

La especificación reserva el parámetro de consulta filter para filtrar datos. Sin embargo, no exige una estrategia de filtrado específica. Los servidores pueden implementar cualquier estrategia, como filter[attribute]=value o un filtrado basado en expresiones más complejo.

Ejemplo (recomendado por JSON:API, pero no obligatorio):

GET /comments?filter[post]=1 (Obtener comentarios para la publicación con ID 1)

GET /comments?filter[post]=1,2&filter[author]=12 (Obtener comentarios para las publicaciones 1 o 2, por el autor 12)

Los clientes deben consultar la documentación de la API para comprender sus capacidades de filtrado específicas.

Modificando datos: Creando, actualizando y eliminando recursos

JSON:API define protocolos claros para las operaciones de manipulación de datos.

Creando recursos

Para crear un recurso, un cliente envía una solicitud POST a una URL que representa una colección de recursos. El cuerpo de la solicitud debe contener un único objeto de recurso con type y, opcionalmente, attributes y relationships. El cliente no debe proporcionar un id para el nuevo recurso (a menos que se admitan y habiliten los ID generados por el cliente).

Solicitud:

POST /articles

Accept: application/vnd.api+json

Content-Type: application/vnd.api+jsonJSON

{
  "data": {
    "type": "articles",
    "attributes": {
      "title": "Título del Nuevo Artículo",
      "body": "Contenido del nuevo artículo."
    },
    "relationships": {
      "author": {
        "data": { "type": "users", "id": "42" }
      }
    }
  }
}

Respuestas exitosas:

Si se intenta crear un recurso con un ID generado por el cliente que ya existe, y el servidor no admite la actualización a través de POST, debe devolver 409 Conflict.

Actualizando recursos

Los recursos se actualizan utilizando el método HTTP PATCH. La solicitud debe incluir el id del recurso a actualizar. El cuerpo de la solicitud contiene un objeto de recurso con type, id y los attributes y/o relationships a actualizar.

Solicitud para actualizar el título de un artículo y una de sus relaciones:

PATCH /articles/1

Accept: application/vnd.api+json

Content-Type: application/vnd.api+jsonJSON{ "data": { "type": "articles", "id": "1", "attributes": { "title": "Título del Artículo Actualizado" }, "relationships": { "tags": { "data": [ { "type": "tags", "id": "3" }, { "type": "tags", "id": "4" } ] } } } }

Puntos clave para las actualizaciones:

Respuestas exitosas:

Si el recurso a actualizar no existe, el servidor debe devolver 404 Not Found.

Actualizando relaciones directamente

JSON:API proporciona formas específicas para gestionar relaciones sin afectar los atributos del recurso primario.

{
  "data": { "type": "users", "id": "24" } // Asignar nuevo autor o null para borrar
}
{
  "data": [
    { "type": "comments", "id": "101" },
    { "type": "comments", "id": "102" }
  ]
}
{
  "data": [
    { "type": "comments", "id": "103" }
  ]
}
{
  "data": [
    { "type": "comments", "id": "5" }
  ]
}

Las actualizaciones de relaciones exitosas suelen devolver 200 OK o 204 No Content.

Eliminando recursos

Para eliminar un recurso, un cliente envía una solicitud DELETE al endpoint del recurso.

Solicitud:

DELETE /articles/1

Accept: application/vnd.api+json

Respuesta exitosa:

Si el recurso no existe, el servidor debería devolver 404 Not Found. La especificación no dicta cómo deben manejarse los recursos o relaciones relacionadas al eliminar (por ejemplo, eliminaciones en cascada); este es un detalle de implementación.

Manejo de errores

Cuando ocurre un error, los servidores deben usar códigos de estado HTTP apropiados (4xx para errores del cliente, 5xx para errores del servidor). El cuerpo de la respuesta debería contener un documento de error JSON:API.

Un documento de error incluye un miembro errors de nivel superior, que es un array de objetos de error. Cada objeto de error puede contener:

Ejemplo de respuesta de error (422 Unprocessable Entity):JSON

{
  "errors": [
    {
      "status": "422",
      "source": { "pointer": "/data/attributes/email" },
      "title": "Atributo Inválido",
      "detail": "La dirección de correo electrónico no es válida."
    },
    {
      "status": "422",
      "source": { "pointer": "/data/relationships/author" },
      "title": "Valor de Relación Inválido",
      "detail": "El autor con ID '999' no existe."
    }
  ]
}

Consideraciones del lado del servidor y mejores prácticas

Si bien la especificación central es completa, JSON:API también proporciona recomendaciones para aspectos como el diseño de URL y la nomenclatura de miembros.

Diseño de URL

Nomenclatura de miembros

Extendiendo la especificación: Extensiones y perfiles

JSON:API está diseñado para ser extensible para satisfacer necesidades cambiantes y casos de uso específicos.

Extensiones

Las extensiones pueden introducir nueva funcionalidad no cubierta por la especificación base. Un ejemplo es la extensión "Atomic Operations", que permite realizar múltiples operaciones (crear, actualizar, eliminar) en una única solicitud atómica. Tanto el cliente como el servidor deben comprender una extensión para que pueda ser utilizada. Si un servidor recibe una solicitud con una extensión no admitida, debe responder con un error apropiado.

Perfiles

Los perfiles definen un conjunto de convenciones sobre la especificación base para un caso de uso particular (por ejemplo, una forma específica de manejar marcas de tiempo o un conjunto común de atributos meta). A diferencia de las extensiones, los perfiles pueden ignorarse de forma segura si una de las partes no los comprende. Están destinados a promover la interoperabilidad para patrones comunes sin requerir cambios en la especificación central o exigir soporte universal.

Los servidores pueden anunciar las extensiones y perfiles admitidos en el objeto jsonapi de nivel superior. Esto permite a los clientes descubrir estas capacidades y adaptar sus solicitudes en consecuencia.

El futuro de JSON:API

JSON:API continúa evolucionando, impulsado por la contribución de la comunidad y la necesidad de abordar los desafíos emergentes del diseño de API. Su enfoque en la convención sobre la configuración, la eficiencia y la experiencia del desarrollador ha consolidado su lugar como un estándar líder para construir APIs modernas. Al adoptar JSON:API, los equipos de desarrollo pueden reducir significativamente la ambigüedad, mejorar la interoperabilidad y acelerar el ritmo de desarrollo y consumo de APIs.

Esta exploración detallada cubre la gran mayoría de la especificación JSON:API. Al comprender e implementar estos principios, los desarrolladores pueden crear APIs que no solo sean funcionales, sino también limpias, consistentes y un placer trabajar con ellas, fomentando en última instancia un ecosistema de API más productivo y colaborativo.

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs