Saltar al contenido principal

Clase 02 — REST: Principios y Diseño de APIs

¿Qué es REST?​

REST (Representational State Transfer) es un estilo de arquitectura para diseñar APIs, definido por Roy Fielding en el año 2000. No es un estándar ni un protocolo — es un conjunto de restricciones que, si las cumplís, tu API es "RESTful".

Los 6 principios de REST​

1. Cliente-Servidor​

Separación clara entre quien consume (cliente) y quien sirve (servidor).

2. Sin estado (Stateless)​

Cada petición contiene toda la información necesaria. El servidor no recuerda peticiones anteriores.

# ✅ Stateless — Cada petición lleva su token
curl -H "Authorization: Bearer MI_TOKEN" https://api.com/datos

# ❌ Stateful — Depende de estado en servidor (sesiones)

3. Cacheable​

Las respuestas deben indicar si se pueden cachear.

curl -sI https://api.github.com/users/octocat | grep -iE "cache|etag|last-modified"

4. Interfaz uniforme​

Reglas consistentes: recursos por URI, manipulación con JSON, mensajes auto-descriptivos, HATEOAS.

5. Sistema en capas​

Cliente → CDN → Load Balancer → API Gateway → Servidor → Base de datos

6. Código bajo demanda (opcional)​

El servidor puede enviar código ejecutable al cliente.

Diseño de URLs RESTful​

# Regla 1: Usar sustantivos (no verbos)
✅ GET /api/usuarios
❌ GET /api/obtenerUsuarios

# Regla 2: Plural para colecciones
✅ GET /api/usuarios
❌ GET /api/usuario

# Regla 3: Minúsculas y guiones
✅ GET /api/tipos-pokemon
❌ GET /api/TiposPokemon

# Regla 4: Jerárquía para relaciones
✅ GET /api/usuarios/42/posts

# Regla 5: Query params para filtrar/ordenar/paginar
✅ GET /api/pokemon?tipo=fire&limit=10&sort=nombre

# Regla 6: Versionado en la URL
✅ GET /api/v2/usuarios

Patrones de URLs​

# Colección
GET /api/v2/pokemon # Listar todos
POST /api/v2/pokemon # Crear uno nuevo

# Recurso individual
GET /api/v2/pokemon/25 # Obtener Pikachu
PUT /api/v2/pokemon/25 # Reemplazar Pikachu
PATCH /api/v2/pokemon/25 # Actualizar parcialmente
DELETE /api/v2/pokemon/25 # Eliminar Pikachu

# Sub-recursos
GET /api/v2/pokemon/25/moves # Movimientos de Pikachu
GET /api/v2/pokemon/25/stats # Stats de Pikachu

# Acciones especiales (cuando CRUD no basta)
POST /api/v2/pokemon/25/evolve # Acción: evolucionar
POST /api/v2/deploy/trigger # Acción: trigger deploy

Formato de respuestas​

JSON estándar — Recurso individual​

{
"id": 25,
"nombre": "pikachu",
"tipo": "electric",
"nivel": 42,
"creado_en": "2026-01-15T10:30:00Z",
"actualizado_en": "2026-02-20T08:15:00Z"
}

Colección con paginación​

{
"data": [
{"id": 1, "nombre": "bulbasaur", "tipo": "grass"},
{"id": 2, "nombre": "ivysaur", "tipo": "grass"},
{"id": 3, "nombre": "venusaur", "tipo": "grass"}
],
"pagination": {
"total": 1025,
"page": 1,
"per_page": 3,
"total_pages": 342,
"next": "/api/v2/pokemon?page=2&per_page=3",
"prev": null
}
}

Error estándar​

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos enviados no son válidos",
"details": [
{"field": "email", "message": "Formato de email inválido"},
{"field": "nombre", "message": "El nombre es obligatorio"}
]
},
"request_id": "req-abc-123",
"timestamp": "2026-02-20T10:00:00Z"
}

HATEOAS — Hipervínculos en respuestas​

# PokéAPI implementa HATEOAS
curl -s https://pokeapi.co/api/v2/pokemon/25 | jq '{
nombre: .name,
species_url: .species.url,
tipos: [.types[].type | {nombre: .name, url: .url}]
}'
# Cada recurso incluye URLs para navegar a recursos relacionados

Paginación​

Estilos de paginación​

# 1. Offset-based (la más común)
GET /api/pokemon?page=2&per_page=20
GET /api/pokemon?offset=20&limit=20

# 2. Cursor-based (mejor para datos que cambian)
GET /api/pokemon?cursor=abc123&limit=20

# 3. Keyset (la más eficiente para bases de datos grandes)
GET /api/pokemon?after_id=100&limit=20

Implementar paginación con curl​

#!/bin/bash
API="https://pokeapi.co/api/v2/pokemon"
PAGE=1
LIMIT=5

while true; do
OFFSET=$(( (PAGE - 1) * LIMIT ))
response=$(curl -s "${API}?limit=${LIMIT}&offset=${OFFSET}")

count=$(echo "$response" | jq '.results | length')
next=$(echo "$response" | jq -r '.next')

echo "Página $PAGE (${count} resultados):"
echo "$response" | jq -r '.results[].name' | sed 's/^/ /'

[[ "$next" == "null" || $PAGE -ge 3 ]] && break
((PAGE++))
done

Filtrado, Ordenamiento y Búsqueda​

# Filtrado
GET /api/pokemon?tipo=fire&generacion=1

# Ordenamiento
GET /api/pokemon?sort=nombre # Ascendente
GET /api/pokemon?sort=-nombre # Descendente

# Búsqueda
GET /api/pokemon?q=pika

# Selección de campos
GET /api/pokemon?fields=id,nombre,tipo

# Ejemplo real con GitHub API
curl -s "https://api.github.com/search/repositories?q=kubernetes+language:go&sort=stars&order=desc&per_page=5" | \
jq '.items[] | {name: .full_name, stars: .stargazers_count, language: .language}'

Versionado de APIs​

# 1. En la URL (más común)
GET /api/v1/pokemon
GET /api/v2/pokemon

# 2. En el header
GET /api/pokemon
Accept: application/vnd.miapi.v2+json

# 3. Query parameter
GET /api/pokemon?version=2

Ejercicios​

  1. Diseñá las URLs REST para una API de gestión de tareas (CRUD + filtros + paginación)
  2. Hacé peticiones paginadas a la PokéAPI y obtené los primeros 50 Pokémon
  3. Usá la GitHub API para buscar repos por lenguaje, ordenados por estrellas
  4. Analizá la respuesta de https://pokeapi.co/api/v2/pokemon/25 e identificá HATEOAS
  5. Compará la estructura de las respuestas JSON de 3 APIs diferentes