Clase 06 — APIs en la Práctica: Servicios Reales
APIs que todo DevOps debe conocer
1. GitHub API
GH="https://api.github.com"
AUTH=(-H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json")
# Tu perfil
curl -s "${AUTH[@]}" "$GH/user" | jq '{login, name, public_repos, followers}'
# Crear repo
curl -s -X POST "${AUTH[@]}" "$GH/user/repos" \
-d '{"name":"test-api","private":true,"auto_init":true}' | jq '.html_url'
# Crear issue
curl -s -X POST "${AUTH[@]}" "$GH/repos/OWNER/REPO/issues" \
-d '{"title":"Bug via API","body":"Descripción","labels":["bug"]}' | jq '.html_url'
# Trigger GitHub Actions workflow
curl -s -X POST "${AUTH[@]}" \
"$GH/repos/OWNER/REPO/actions/workflows/deploy.yml/dispatches" \
-d '{"ref":"main","inputs":{"environment":"staging"}}'
# Listar workflow runs
curl -s "${AUTH[@]}" "$GH/repos/OWNER/REPO/actions/runs?per_page=5" | \
jq '.workflow_runs[] | {id, name, status, conclusion, created_at}'
2. Docker Hub / Registry API
# Obtener token para imagen pública
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/nginx:pull" | jq -r '.token')
# Listar tags
curl -s -H "Authorization: Bearer $TOKEN" \
"https://registry-1.docker.io/v2/library/nginx/tags/list" | jq '.tags[:10]'
# Docker socket API (local)
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | \
jq '.[] | {id: .Id[0:12], name: .Names[0], status: .Status, image: .Image}'
3. Kubernetes API
# Desde dentro de un pod
K8S="https://kubernetes.default.svc"
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
curl -s --cacert "$CA" -H "Authorization: Bearer $TOKEN" \
"$K8S/api/v1/namespaces/default/pods" | jq '.items[].metadata.name'
# Desde fuera (con kubectl proxy)
kubectl proxy &
PROXY="http://localhost:8001"
curl -s "$PROXY/api/v1/namespaces" | jq '[.items[].metadata.name]'
curl -s "$PROXY/apis/apps/v1/namespaces/default/deployments" | \
jq '.items[] | {name: .metadata.name, replicas: .spec.replicas, ready: .status.readyReplicas}'
# Escalar un deployment
curl -s -X PATCH "$PROXY/apis/apps/v1/namespaces/default/deployments/mi-app" \
-H "Content-Type: application/merge-patch+json" \
-d '{"spec":{"replicas":5}}' | jq '.spec.replicas'
4. Terraform Cloud API
TF="https://app.terraform.io/api/v2"
TF_AUTH=(-H "Authorization: Bearer $TFC_TOKEN" -H "Content-Type: application/vnd.api+json")
curl -s "${TF_AUTH[@]}" "$TF/organizations/mi-org/workspaces" | \
jq '.data[] | {name: .attributes.name, version: .attributes."terraform-version"}'
Webhooks — APIs inversas
Los webhooks son "APIs al revés": el servidor te envía datos a vos cuando ocurre un evento.
API normal: Vos → Petición → Servidor → Respuesta → Vos
Webhook: Evento → Servidor → POST → Tu endpoint → Procesás
Recibir webhooks con FastAPI
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/webhook/github")
async def github_webhook(request: Request):
event = request.headers.get("X-GitHub-Event", "unknown")
payload = await request.json()
if event == "push":
repo = payload["repository"]["full_name"]
branch = payload["ref"].split("/")[-1]
commits = len(payload.get("commits", []))
print(f"🔔 Push a {repo}/{branch}: {commits} commits")
elif event == "pull_request":
action = payload["action"]
pr = payload["pull_request"]
print(f"🔔 PR #{pr['number']} {action}: {pr['title']}")
return {"status": "received", "event": event}
Enviar webhooks
# Slack
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d '{"text": "🚀 Deploy v2.0 completado en producción"}'
# Discord
curl -s -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d '{"content": "Deploy exitoso", "embeds": [{"title": "v2.0", "color": 3066993}]}'
APIs GraphQL (introducción)
# GraphQL usa un solo endpoint y POST
curl -s -X POST https://api.github.com/graphql \
-H "Authorization: bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query { viewer { login name repositories(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { name stargazerCount } } } }"
}' | jq '.data'
REST vs GraphQL
| Aspecto | REST | GraphQL |
|---|---|---|
| Endpoints | Múltiples (/users, /posts) | Uno solo (/graphql) |
| Datos | Respuesta fija del servidor | Cliente pide exactamente lo que necesita |
| Over-fetching | Común | No (solo lo que pedís) |
| Cache | Fácil (por URL) | Complejo |
| Documentación | OpenAPI/Swagger | Schema introspection |
Ejemplo: Monitor multi-servicio
#!/bin/bash
check_endpoint() {
local nombre="$1" url="$2" expected="$3"
local start http_code duration
start=$(date +%s%N)
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url")
duration=$(( ($(date +%s%N) - start) / 1000000 ))
if [[ "$http_code" == "$expected" ]]; then
printf " ✅ %-25s HTTP %-4s %4dms\n" "$nombre" "$http_code" "$duration"
else
printf " ❌ %-25s HTTP %-4s (esperado: %s)\n" "$nombre" "$http_code" "$expected"
fi
}
echo "API Infrastructure Monitor — $(date '+%Y-%m-%d %H:%M:%S')"
echo "══════════════════════════════════════════════"
check_endpoint "GitHub API" "https://api.github.com" "200"
check_endpoint "PokéAPI" "https://pokeapi.co/api/v2/pokemon/1" "200"
check_endpoint "JSONPlaceholder" "https://jsonplaceholder.typicode.com/posts/1" "200"
check_endpoint "httpbin" "https://httpbin.org/get" "200"
Ejercicios
- Usá la GitHub API para: crear un repo, agregar un archivo, crear un issue
- Consultá la Docker Hub API para listar tags de
nginxypython - Creá un webhook receiver con FastAPI que procese eventos de push
- Escribí un monitor que verifique 5 APIs y envíe alertas a Slack si alguna falla
- Hacé una query GraphQL a la GitHub API para obtener info de un repo con sus issues