REST es un estilo de arquitectura para servicios web.
JSON es el formato de datos más usado en REST por su ligereza y compatibilidad.
La app cliente (web/móvil/otro servicio) envía HTTP requests y la API responde con HTTP responses (status code + headers + cuerpo JSON).
Componentes clave:
https://api.ejemplo.com/productos/101
application/json
(si envías body)
application/json
(lo que esperas recibir)
Bearer <token>
(si hay auth)
Ejemplo POST (crear)
POST /productos HTTP/1.1
Host: api.ejemplo.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer <token>
{
"descripcion": "Teclado mecánico",
"precio": 1200.50,
"disponible": true
}
application/json; charset=utf-8
)Ejemplo 201 Created
HTTP/1.1 201 Created
Content-Type: application/json
Location: https://api.ejemplo.com/productos/101
{
"id": 101,
"descripcion": "Teclado mecánico",
"precio": 1200.50,
"disponible": true
}
# GET (listar)
curl -H "Accept: application/json" https://api.ejemplo.com/productos
# POST (crear)
curl -X POST https://api.ejemplo.com/productos \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{"descripcion":"Teclado","precio":1200.5,"disponible":true}'
Referencia: curl.
// GET
const lista = await fetch("/productos", { headers: { Accept: "application/json" } })
.then(r => r.json());
// POST
const nuevo = await fetch("/productos", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({ descripcion: "Mouse", precio: 850.75, disponible: false })
}).then(r => r.json());
Docs: fetch.
import requests
r = requests.get("https://api.ejemplo.com/productos", headers={"Accept": "application/json"})
productos = r.json()
nuevo = {"descripcion": "Monitor 24", "precio": 1500.0, "disponible": True}
r = requests.post("https://api.ejemplo.com/productos",
json=nuevo,
headers={"Accept":"application/json"})
data = r.json()
Docs: requests.
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri("https://api.ejemplo.com/") };
var productos = await http.GetFromJsonAsync<List<Producto>>("productos");
var creado = await http.PostAsJsonAsync("productos", new { descripcion="Parlantes", precio=999.99, disponible=true });
var body = await creado.Content.ReadFromJsonAsync<Producto>();
Docs: System.Net.Http.Json.
{
"id": 101,
"descripcion": "Teclado mecánico",
"precio": 1200.5,
"disponible": true
}
Query params comunes: ?page=2&limit=20&sort=precio:desc&search=teclado
{
"data": [
{ "id": 101, "descripcion": "Teclado", "precio": 1200.5, "disponible": true },
{ "id": 145, "descripcion": "Teclado RGB", "precio": 1800.0, "disponible": true }
],
"pagination": { "page": 2, "limit": 20, "total": 87, "pages": 5 },
"filters": { "search": "teclado", "sort": "precio:desc" }
}
Plano: devolvés la lista directo (más simple). Envoltorio: data + pagination + meta (más extensible). Elegí uno y sé consistente.
Usá un formato consistente y legible. Referencia útil: RFC 7807 (application/problem+json
).
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://api.ejemplo.com/errors/validation",
"title": "Error de validación",
"status": 400,
"detail": "El campo 'precio' debe ser mayor a 0.",
"errors": {
"precio": ["Debe ser mayor a 0"]
}
}
Authorization: Bearer <token>
x-api-key: <clave>
Respuestas ante problemas: 401 Unauthorized (no autenticado) y 403 Forbidden (autenticado pero sin permisos).
Permite que un frontend en dominio A consuma API en dominio B. Ver CORS.
Respuestas de la API deben incluir headers como:
Access-Control-Allow-Origin: https://mi-frontend.com
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
/v1/productos
Accept: application/vnd.miapi.v2+json
Headers: Cache-Control
, ETag
, Last-Modified
. Ver caché HTTP.
Flujo típico:
Server responde 200 OK + ETag: "abc123".
Cliente reintenta con If-None-Match: "abc123".
Si no cambió: 304 Not Modified (sin body).
Dos estilos comunes:
application/merge-patch+json
): enviás solo campos a cambiar. Especificación: RFC 7396.application/json-patch+json
): operaciones (add, replace, remove). Especificación: RFC 6902.Ejemplo Merge Patch
PATCH /productos/101
Content-Type: application/merge-patch+json
{ "precio": 1399.99 }
Define reglas de tipos, requeridos y rangos. Útil para contratos y tests (contract testing). Ver JSON Schema.
Recursos
GET /productos lista (paginada)
GET /productos/{id} detalle
POST /productos crear
PUT /productos/{id} reemplazar
PATCH /productos/{id} actualizar parcial
DELETE /productos/{id} borrar
Modelo (JSON)
{
"id": 101,
"descripcion": "Teclado mecánico",
"precio": 1200.5,
"disponible": true,
"tags": ["periférico", "oferta"]
}
Respuesta paginada (GET /productos?page=1&limit=2
)
{
"data": [
{ "id": 101, "descripcion": "Teclado", "precio": 1200.5, "disponible": true },
{ "id": 102, "descripcion": "Mouse", "precio": 850.75, "disponible": false }
],
"pagination": { "page": 1, "limit": 2, "total": 37, "pages": 19 }
}
Error de validación (POST)
{
"type": "https://api.ejemplo.com/errors/validation",
"title": "Error de validación",
"status": 400,
"errors": {
"descripcion": ["Debe tener al menos 3 caracteres"],
"precio": ["Debe ser mayor a 0"]
}
}
application/json
.JSON es el formato rey de las APIs REST. Dominar headers, métodos, status codes y estructuras de respuesta te permite diseñar y consumir APIs sólidas. Sumá paginación, errores consistentes, validación y seguridad para un API profesional.