Clase 07 — JSON y APIs REST
Las APIs REST usan JSON como formato principal de comunicación. Desde la terminal usamos curl + jq para interactuar con ellas.
curl + jq — Peticiones HTTP
# GET — Obtener datos
curl -s https://pokeapi.co/api/v2/pokemon/pikachu | jq '{
nombre: .name, id: .id,
tipos: [.types[].type.name],
peso: .weight, altura: .height
}'
# POST — Enviar datos
curl -s -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"nombre": "RoxsRoss", "rol": "DevOps Engineer"}' | jq '.json'
# PUT — Actualizar datos
curl -s -X PUT https://httpbin.org/put \
-H "Content-Type: application/json" \
-d '{"id": 1, "nombre": "Ana García", "rol": "Senior Dev"}' | jq '.json'
# DELETE — Eliminar datos
curl -s -X DELETE https://httpbin.org/delete | jq '.headers'
# Ver status code
curl -s -o /dev/null -w "%{http_code}" https://pokeapi.co/api/v2/pokemon/pikachu
Ejemplo: Pokédex CLI
#!/bin/bash
POKEMON="${1:-pikachu}"
BASE_URL="https://pokeapi.co/api/v2/pokemon"
response=$(curl -s -w "\n%{http_code}" "$BASE_URL/$POKEMON")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
echo "Error: Pokémon '$POKEMON' no encontrado (HTTP $http_code)"
exit 1
fi
echo "$body" | jq -r '
"========================================",
" POKÉDEX #\(.id): \(.name | ascii_upcase)",
"========================================",
"Tipos : \([.types[].type.name] | join(", "))",
"Peso : \(.weight / 10) kg",
"HP : \(.stats[] | select(.stat.name == "hp") | .base_stat)",
"Ataque : \(.stats[] | select(.stat.name == "attack") | .base_stat)",
"Velocidad: \(.stats[] | select(.stat.name == "speed") | .base_stat)",
"========================================"'
Ejemplo: GitHub API
# Info de un repositorio
curl -s https://api.github.com/repos/roxsross/devops | jq '{
nombre: .full_name, estrellas: .stargazers_count, lenguaje: .language
}'
# Listar issues abiertos
curl -s "https://api.github.com/repos/kubernetes/kubernetes/issues?state=open&per_page=5" | \
jq '.[] | {numero: .number, titulo: .title, autor: .user.login}'
# Con autenticación (más rate limit)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/user | jq '{login, name, public_repos}'
# Crear un issue
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/OWNER/REPO/issues" \
-d '{"title":"Bug via API","body":"Descripción","labels":["bug"]}' | jq '{number, html_url}'
Script de health check
#!/bin/bash
SERVICIOS='[
{"nombre": "API", "url": "https://httpbin.org/status/200"},
{"nombre": "Auth", "url": "https://httpbin.org/status/200"},
{"nombre": "DB Health", "url": "https://httpbin.org/status/503"}
]'
echo " HEALTH CHECK - $(date '+%Y-%m-%d %H:%M')"
echo "======================================"
echo "$SERVICIOS" | jq -c '.[]' | while read -r servicio; do
nombre=$(echo "$servicio" | jq -r '.nombre')
url=$(echo "$servicio" | jq -r '.url')
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url")
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
printf " ✅ %-15s → OK (HTTP %s)\n" "$nombre" "$http_code"
else
printf " ❌ %-15s → FAIL (HTTP %s)\n" "$nombre" "$http_code"
fi
done
Webhook de Slack/Discord
# Enviar mensaje a Slack
enviar_slack() {
local mensaje="$1" color="${2:-#36a64f}"
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg text "$mensaje" --arg color "$color" \
'{attachments: [{color: $color, text: $text, footer: "DevOps Bot"}]}')"
}
enviar_slack "Deploy exitoso en producción v2.1.0" "#36a64f"
enviar_slack "ERROR: Pipeline falló en stage test" "#dc3545"
Wrapper de API con manejo de errores
api_request() {
local method="$1" endpoint="$2" data="$3"
local headers=(-H "Content-Type: application/json" -H "Accept: application/json")
[[ -n "$TOKEN" ]] && headers+=(-H "Authorization: Bearer $TOKEN")
local response=$(curl -s -w "\n%{http_code}" -X "$method" \
"${headers[@]}" ${data:+-d "$data"} "${BASE_URL}${endpoint}")
local http_code=$(echo "$response" | tail -1)
local body=$(echo "$response" | sed '$d')
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
echo "$body" | jq '.'; return 0
else
echo "Error HTTP $http_code:" >&2
echo "$body" | jq '.' 2>/dev/null || echo "$body" >&2; return 1
fi
}
Paginación
#!/bin/bash
pagina=1
while true; do
response=$(curl -s "https://pokeapi.co/api/v2/pokemon?offset=$(( (pagina-1) * 20 ))&limit=20")
echo "Página $pagina:"
echo "$response" | jq -r '.results[].name'
siguiente=$(echo "$response" | jq -r '.next')
[[ "$siguiente" == "null" ]] && break
((pagina++))
[[ $pagina -gt 3 ]] && break
done