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