Cómo diseñar APIs para defender cualquier sistema
Plantilla práctica, casos reales y conceptos críticos para Google: REST vs gRPC (lo que usan internamente), autenticación, idempotencia, paginación, real-time. Presiona / para buscar.
1 Framework para diseñar APIs
Sigue estos 7 pasos cuando te pidan "diseña el API de X". Verbaliza cada uno.
📐 Resource-oriented (Google AIP)
Google prefiere APIs resource-oriented: las URLs son recursos, los métodos HTTP son verbos. POST /users mejor que POST /createUser.
📛 Naming conventions
- Recursos en plural:
/users - IDs en path:
/users/{userId} - Sub-recursos:
/users/{id}/posts kebab-caseen paths,camelCaseen JSON- Verbos custom como acciones:
POST /videos/{id}:publish
🎯 Frase de oro al iniciar
"Voy a empezar identificando los recursos principales, luego las operaciones por recurso, y de ahí derivo los endpoints. Después hablo de paginación, errores, auth y rate limiting."
2 Métodos HTTP & Status Codes
Los fundamentos. Si confundes idempotencia o status codes, pierdes credibilidad de inmediato.
Métodos HTTP — qué hacen, cuándo, idempotencia
| Método | Uso | Ejemplo | Idempotente | Status típico |
|---|---|---|---|---|
| GET | Leer un recurso o lista. Cacheable. Nunca modifica estado. | GET /api/users/u_42 |
Sí Safe + idempotent | 200 404 |
| POST | Crear recurso o disparar acción. Server asigna ID. | POST /api/users |
No Usa Idempotency-Key |
201 202 |
| PUT | Reemplazar recurso completo. Cliente puede asignar ID. | PUT /api/users/u_42 |
Sí Mismo body = mismo estado | 200 204 |
| PATCH | Actualizar parcial. Envía solo los campos a cambiar. | PATCH /api/users/u_42 |
Depende Si es JSON-Merge: sí | 200 409 |
| DELETE | Eliminar (hard) o soft-delete con tombstone. | DELETE /api/users/u_42 |
Sí Borrar dos veces = igual | 204 404 |
| HEAD | Como GET pero solo headers. Para checks de existencia. | HEAD /api/files/abc |
Sí | 200 404 |
| OPTIONS | Capabilities y CORS preflight. | OPTIONS /api/users |
Sí | 204 |
Status Codes — los que SÍ se preguntan
| Código | Nombre | Cuándo usarlo | Trampa / nota |
|---|---|---|---|
| 200 | OK | Éxito con body. Para GET, PUT, PATCH, DELETE con respuesta. | Default success. No abuses si no hay body — usa 204. |
| 201 | Created | POST exitoso que creó recurso. Devuelve Location header. |
Solo para creación. POST que dispara acción usa 200/202. |
| 202 | Accepted | Aceptado para procesamiento async. Devuelve job ID o status URL. | Crítico para uploads, batch, transcoding. Mucho mejor que dejar el cliente colgado. |
| 204 | No Content | Éxito sin body. Para DELETE, PUT que no devuelven nada. | Sin body literal. Si quieres devolver el recurso actualizado, usa 200. |
| 301 | Moved Permanently | Recurso movido de URL para siempre. Cacheable. | URL shortener clásico usa 301 (cacheado por browser). |
| 302 | Found | Redirect temporal. URL puede cambiar después. | Para login flows, A/B test redirects. |
| 304 | Not Modified | Cliente envió If-None-Match o If-Modified-Since y el recurso no cambió. |
Ahorra bandwidth. Esencial con ETags. |
| 400 | Bad Request | Request mal formado, JSON inválido, parámetros faltantes. | Genérico. Para validación específica de campos, considera 422. |
| 401 | Unauthorized | No autenticado o token inválido/expirado. | Mal nombrado: significa "no auth", no "no permiso". Devuelve WWW-Authenticate. |
| 403 | Forbidden | Autenticado pero sin permiso para este recurso. | Importante distinción con 401. Algunos prefieren 404 para no revelar existencia. |
| 404 | Not Found | Recurso no existe o user no tiene permiso de saber. | Útil para no filtrar info sobre recursos privados. |
| 409 | Conflict | Conflicto de estado: duplicate, version mismatch (ETag), resource locked. | Optimistic concurrency con If-Match → 409 si cambió. |
| 410 | Gone | Recurso existió, fue eliminado intencionalmente, no volverá. | Para deprecation o GDPR delete. Mejor que 404 para SEO. |
| 422 | Unprocessable Entity | Sintaxis OK pero validación de negocio falló. Email inválido, edad negativa. | Ideal con cuerpo de errores por campo (ver sección 5). |
| 429 | Too Many Requests | Rate limit excedido. | Devuelve Retry-After. Imprescindible. |
| 500 | Internal Server Error | Bug genérico. NO incluyas stack traces en prod. | Default cuando nada más aplica. Logueas, alertas, regresas correlationId. |
| 502 | Bad Gateway | Upstream service devolvió error o no responde. | Reverse proxy / API Gateway clásico cuando backend muere. |
| 503 | Service Unavailable | Sobrecarga, maintenance, o circuit breaker abierto. | Devuelve Retry-After. Cliente debe hacer backoff. |
| 504 | Gateway Timeout | Upstream no respondió a tiempo. | Diferenciar de 503: aquí sí intentaste, pero timeout. |
3 REST vs gRPC vs GraphQL
Google usa gRPC internamente. Mencionar gRPC bien sumará puntos.
| Aspecto | REST | gRPC | GraphQL |
|---|---|---|---|
| Transporte | HTTP/1.1 (típico) | HTTP/2 (multiplexed, header compression) | HTTP/1.1 (típico) |
| Formato | JSON (texto) | Protocol Buffers (binario, ~3-10× más pequeño) | JSON |
| Schema | OpenAPI opcional | .proto file obligatorio |
GraphQL SDL obligatorio |
| Performance | Buena | Excelente (binario + HTTP/2) | Variable (resolver overhead) |
| Streaming | Limitado (SSE, chunked) | Bidirectional nativo (4 modos) | Subscriptions (vía WebSocket) |
| Browser support | Nativo | Necesita gRPC-Web + proxy | Nativo |
| Caching | Excelente (HTTP cache, CDN, ETag) | No nativo (HTTP/2 push o app-level) | Difícil (queries únicas) |
| Code-gen | Manual u OpenAPI generators | Automático en muchos lenguajes | Tools maduras (Apollo, urql) |
| Mejor para | APIs públicas, integraciones B2B, mobile. | Microservicios internos, baja latencia, polyglot. | Frontends con muchas vistas, agregación. |
| Uso en Google | Cloud APIs públicas (con gRPC subyacente) | Default interno · Stubby precursor | Menos común |
🟦 Cuándo elegir REST
- API pública, partners externos
- Necesitas caching agresivo (CDN)
- Clientes heterogéneos (curl, browsers)
- Recursos claros, CRUD-heavy
- Discoverability importa (OpenAPI)
🟩 Cuándo elegir gRPC
- Microservicios internos
- Latencia crítica (mobile, edge)
- Streaming bidireccional (chat, video)
- Polyglot teams (auto-gen clients)
- Schema evolution importante
🟪 Cuándo elegir GraphQL
- Frontends con muchas vistas distintas
- Reducir over-fetching móvil
- Aggregar múltiples backends (BFF)
- Schema-driven dev workflow
- Cuidado: N+1 queries, caching difícil
📡 gRPC streaming modes
- Unary: request → response (como REST normal)
- Server streaming: 1 request → N responses (feed, logs)
- Client streaming: N requests → 1 response (upload chunks)
- Bidirectional: N ↔ N (chat, gaming, real-time)
🇬 Tip Google-specific
Google publicó las API Improvement Proposals (AIP) en aip.dev — son sus reglas internas. Menciona resource-oriented design, standard methods (List, Get, Create, Update, Delete), field masks para updates parciales (en lugar de PATCH), y nombres pluralizados.
4 Autenticación & Autorización
Auth = quién eres. Authz = qué puedes hacer. No los confundas.
| Mecanismo | Cómo funciona | Cuándo usarlo | Pros / Contras |
|---|---|---|---|
| API Keys | Header X-API-Key: abc o query param. Server valida en DB. |
APIs server-to-server, integraciones simples, tracking de quotas. | + Simple · − Sin expiración nativa, mismo nivel de acceso, no auditable a nivel user. |
| Basic Auth | Authorization: Basic base64(user:pass) |
Solo internal/testing sobre TLS. | + Trivial · − Credenciales en cada request. Evítalo en producción. |
| JWT (Bearer) | Token firmado (HS256/RS256) con claims. Authorization: Bearer eyJ.... Stateless. |
Microservicios, mobile/web apps, sesiones distribuidas. | + Stateless, escala · − Revocación difícil, tamaño grande, no incluyas datos sensibles. |
| OAuth 2.0 | Delegated auth con scopes. Auth Code (web), Client Credentials (s2s), Device (TVs), PKCE (mobile/SPA). | "Login con Google". APIs de terceros. Scopes granulares. | + Estándar industria · − Complejidad. Implícito está deprecated; usa PKCE. |
| OIDC | Capa de identidad sobre OAuth 2.0. ID Token (JWT) + UserInfo endpoint. | Cuando además de auth necesitas saber quién es el user. | + Identity estándar · − Otra capa más, depende de un IdP. |
| Session cookies | Server crea sesión, manda cookie firmada. State server-side. | Web apps tradicionales, mismo dominio. | + Revocable trivial · − CSRF, escala (Redis sessions), no funciona cross-domain. |
| mTLS | Mutual TLS: cliente y servidor presentan certs. Verificación criptográfica. | Service-to-service en zero-trust networks. PCI, banking, GovCloud. | + Muy seguro, sin secret en banda · − Cert management complejo, rotación. |
| IAM (cloud) | Service accounts con roles. GCP/AWS firma requests con SDK. | Apps corriendo en GCP/AWS hablando con APIs cloud. | + Sin secrets gestionados, audit log automático · − Vendor lock-in. |
🔑 JWT structure
3 partes base64 separadas por puntos:
// Header { "alg": "RS256", "typ": "JWT" } // Payload (claims) { "sub": "user_42", "iss": "auth.example.com", "aud": "api.example.com", "exp": 1735689600, "iat": 1735686000, "scope": "read:users write:posts" } // Signature: RSA-sign(header.payload, private_key)
🛂 Authorization (RBAC vs ABAC)
- RBAC: Roles → Permissions. Simple. "Editor puede actualizar posts"
- ABAC: Atributos contextuales. "Puede leer SI: dueño OR rol = admin OR documento es público"
- ReBAC (Zanzibar): Google Drive style — relaciones (owner, editor, viewer) entre user y recurso. Spanner-backed.
- Tip: En Google interview, menciona Zanzibar si el problema involucra permisos complejos (Drive, Docs).
5 Patrones API críticos
Paginación, versionado, errores, idempotencia, rate limiting. Lo que diferencia diseño junior de senior.
📑 Paginación — 4 estrategias
| Estrategia | Ejemplo | Pros | Contras |
|---|---|---|---|
| Offset/Limit | GET /items?offset=100&limit=20 |
Simple, jump a página N. | Lento en datasets grandes (DB skip), inconsistente si insertas/borras durante pagination. |
| Page-based | GET /items?page=5&size=20 |
Como offset, más amigable para UI. | Mismos problemas que offset. |
| Cursor | GET /items?cursor=eyJpZCI6MTIzfQ&limit=20 |
Estable, escala, opaco al cliente. | No puedes saltar a página N; solo siguiente/anterior. |
| Keyset (seek) | GET /items?after_id=123&limit=20 |
Más rápido (índice), determinístico. | Requiere campo ordenable único; tampoco hay "ir a página 5". |
📤 Response shape recomendado
{ "data": [ /* items */ ], "pagination": { "next_cursor": "eyJpZCI6MTQzfQ==", "has_more": true, "total": 10000 // opcional, costoso } }
🔢 Versionado — 4 estrategias
| Estrategia | Ejemplo | Pros | Contras |
|---|---|---|---|
| URL path | /v1/users · /v2/users |
Visible, fácil debug, fácil routing. Default Google Cloud. | "REST puristas" objetan que la versión está en el recurso. |
| Query param | /users?v=2 |
Fácil rollback con feature flag. | Cache complica, fácil olvidar. |
| Header custom | Accept-Version: 2 |
URL limpia, separación de concerns. | No visible en logs/curl, harder debug. |
| Media type | Accept: application/vnd.api.v2+json |
"REST puro", content negotiation. | Verbose, casi nadie lo usa, herramientas no lo respetan. |
Recomendación: URL path (/v1/). Es lo que Google, Stripe, Twilio y AWS usan. Solo hace breaking changes en major versions; cambios aditivos compatibles van en v1. Anuncia deprecation con header Sunset + 6-12 meses de aviso.
❌ Formato de errores — RFC 7807 + estilo Google
📋 RFC 7807 (Problem Details)
HTTP/1.1 422 Unprocessable Entity Content-Type: application/problem+json { "type": "https://api.x/errors/validation", "title": "Validation failed", "status": 422, "detail": "Email format invalid", "instance": "/users/u_42", "errors": [ { "field": "email", "code": "format" } ] }
🇬 Google API Error Format
HTTP/1.1 404 Not Found { "error": { "code": 404, "message": "User not found", "status": "NOT_FOUND", "details": [{ "@type": "...ErrorInfo", "reason": "USER_NOT_FOUND", "domain": "googleapis.com" }] } }
Reglas de oro: (1) Status code HTTP correcto · (2) Mensaje legible para humano · (3) Código de error machine-readable estable · (4) correlationId para soporte · (5) NUNCA stack traces en prod · (6) En 4xx ayuda al cliente a corregir; en 5xx solo loguea internamente.
🔁 Idempotencia — el patrón que SIEMPRE preguntan
🤔 Por qué importa
El cliente pierde la conexión justo cuando el server procesó el POST. ¿Reintenta? Si lo hace, ¿creará 2 órdenes? La idempotencia previene duplicados en pagos, órdenes, transferencias.
- GET, PUT, DELETE: idempotentes por definición
- POST: NO. Necesita
Idempotency-Key - PATCH: depende de la operación
⚙️ Implementación
- Cliente genera UUID en
Idempotency-Keyheader - Server hash(key + request_fingerprint) → busca en Redis/DB
- Si existe + mismo body → devuelve respuesta cacheada
- Si existe + body distinto → 409 Conflict
- Si no existe → procesa, guarda (key, response, hash) por 24h
- TTL típico: 24-72h
POST /api/payments Idempotency-Key: "client-generated-uuid-abc-123" Content-Type: application/json { "amount": 5000, "to": "acct_xyz" } // Server: si ya vió esa key con mismo body → devuelve la response original // Si vió la key con body distinto → 409 Conflict // Si nunca vió la key → procesa, guarda (key → response) en Redis 24h
🚦 Rate Limiting — algoritmos & headers
🪣 Algoritmos
- Token bucket: permite bursts, refill constante. Default.
- Leaky bucket: tasa fija de salida, suaviza tráfico.
- Fixed window: simple, problema de doble pico al borde.
- Sliding window log: preciso, mucha memoria.
- Sliding window counter: aproximación buena con poca memoria.
📨 Headers obligatorios
HTTP/1.1 200 OK X-RateLimit-Limit: 100 X-RateLimit-Remaining: 37 X-RateLimit-Reset: 1735689600 // Cuando excedes: HTTP/1.1 429 Too Many Requests Retry-After: 42 // segundos
Rate limit por: user, API key, IP, endpoint, o combinación. Distribuido: Redis con INCR + EXPIRE atómico (Lua script). Tiers: free (10 RPM), basic (100), pro (1K), enterprise (custom). Documenta los límites en docs públicas.
6 Real-time & comunicación bidireccional
Chat, notificaciones, live updates, multiplayer. Cuándo elegir cada tecnología.
| Tecnología | Cómo funciona | Cuándo usar | Pros / Contras |
|---|---|---|---|
| Short polling | Cliente hace GET cada N segundos. |
Updates de baja frecuencia (status de un job, dashboard refresh). | + Trivial, funciona con cualquier infra · − Wasteful (la mayoría devuelve nada). |
| Long polling | Server retiene la request hasta que hay update o timeout. | Cuando WS no es opción (firewalls), updates ocasionales. | + Real-time-ish sobre HTTP simple · − Conexiones largas, server resources. |
| SSE | Server-Sent Events. HTTP/1.1 stream. Solo server → client. | Notificaciones, live feed, AI tokens streaming, stock prices. | + Auto-reconnect, simple, funciona con CDN · − Unidireccional. |
| WebSocket | Upgrade desde HTTP. TCP persistente bidireccional. | Chat, gaming, colaboración (Docs), trading, multiplayer. | + Full duplex, baja latencia · − Stateful (sticky LB), proxies a veces, no caching. |
| gRPC streaming | HTTP/2 streams. 4 modos (unary, server, client, bidi). | Servicio interno con streaming + tipos fuertes. | + Eficiente, schema, multiplexing · − Browser necesita gRPC-Web. |
| Webhooks | Server A llama a un endpoint público de server B (HTTP POST). | Stripe → tu app, GitHub events, async callbacks B2B. | + Server-to-server, simple · − Tu endpoint debe ser público + idempotente + verificar firma. |
| Push (FCM/APNs) | Notificación a OS de mobile/desktop. Vendor-mediated. | App cerrada, ahorro de batería. | + Funciona offline · − No garantizado, latencia variable. |
📡 SSE en 30 segundos
// Server response: Content-Type: text/event-stream Cache-Control: no-cache event: update data: {"price": 42.5} event: update data: {"price": 42.7}
🔌 WebSocket scaling
- Sticky LB (mismo cliente → misma instancia)
- Pub/Sub backplane (Redis/Pub-Sub) para fan-out
- Heartbeat (ping/pong) cada 30s
- Reconnect con exponential backoff
- Auth en query/cookie (no header en handshake)
🪝 Webhooks bien hechos
- HMAC signature en header (verifica origen)
- Endpoint idempotente + dedup por event_id
- Retry con exponential backoff (24h)
- Dead letter queue tras N fallos
- Devuelve 200 rápido, procesa async
7 Casos típicos & APIs sugeridas
10 problemas frecuentes. La clave no es memorizar, es reconocer recursos y flujos.
1. E-commerce: top productos
RankingTop 10 productos más comprados en 1h, 4h o 24h.
- Problema central: ranking en ventanas de tiempo.
- Solución: eventos de compra → Pub/Sub → agregador (Dataflow) → Bigtable counters → cache.
- No hagas: SCAN sobre tabla de orders en cada click.
2. Ride-sharing
Uber/LyftClientes piden viaje; drivers cercanos reciben notificación en segundos.
- Problema central: matching geoespacial en tiempo real.
- Solución: location updates, geohash/S2, Pub/Sub, WebSocket/push.
- Riesgo: ubicación vieja, drivers duplicados aceptando el mismo ride (race condition).
3. YouTube / streaming
VideoSubir, transcodificar, almacenar y reproducir videos a gran escala.
- Problema central: ingestión pesada + entrega de baja latencia.
- Solución: signed URL upload → Cloud Storage → Pub/Sub → transcoding workers → CDN.
- No hagas: servir videos directo desde el app server. Eso es CDN territory.
4. Spotify
MusicReproducir música, playlists, búsqueda y recomendaciones.
- Problema central: catálogo + streaming + personalización.
- Solución: metadata DB + Cloud Storage/CDN para audio + search index + reco service.
- Cuidado: licencias por región, cache por usuario, derechos.
5. TikTok feed
Feed rankingMostrar videos cortos personalizados con scroll infinito.
- Problema central: ranking personalizado en baja latencia.
- Solución: candidate generation + ranking model + feature store + cache.
- Cuidado: cold start, diversidad, duplicados, freshness.
6. Google Docs
CollaborationDocumentos editados por varios usuarios al mismo tiempo.
- Problema central: concurrencia y colaboración real-time.
- Solución: OT/CRDT, WebSocket, version vector, conflict resolution.
- Cuidado: lost updates, permisos (Zanzibar), audit log.
7. Recommendations
MLRecomendar productos, videos, canciones o documentos relevantes.
- Problema central: candidates + ranking + feedback loop.
- Solución: eventos de usuario, feature store, modelo offline+online, cache.
- Cuidado: latencia (<100ms), sesgo, cold start, métricas A/B.
8. Link shortener
TinyURL/BitlyURLs largas → links cortos, redirección de baja latencia.
- Problema central: generar IDs cortos únicos, redirigir rápido.
- Solución: base62 de counter (Spanner) o hash + colisión-check; cache; analytics async.
- Cuidado: colisiones, links maliciosos, rate limits.
9. File system (Drive)
Dropbox/GDriveCarpetas, uploads, metadata, versionado, sharing.
- Problema central: metadata tree + blobs grandes + permisos.
- Solución: Spanner/Firestore metadata, Cloud Storage blobs, chunk upload, dedup por hash.
- Cuidado: rename/move atómico, permisos heredados, dedup chunk-level.
10. DBMS / motor de BD
Difícil pero defendibleSistema que recibe queries, indexa, transacciona.
- Problema central: storage engine + indexes + query planner + transactions.
- Solución: B+ trees/LSM, buffer pool, WAL, locks/MVCC, replication.
- Cuidado: ACID, recuperación tras crash, isolation levels.
8 Qué mencionar para verte fuerte
Estas palabras elevan tu respuesta si las conectas con el problema correcto.
| Tema | Cuándo lo mencionas | Frase de entrevista |
|---|---|---|
| Cache | Lecturas frecuentes, rankings, perfiles, feed, metadata. | "Cachearía respuestas calientes con TTL para no servir datos eternamente viejos." |
| Queue / Pub/Sub | Procesos pesados, emails, transcoding, analytics, indexing. | "Lo haría asíncrono con Pub/Sub para desacoplar el request del procesamiento pesado." |
| CDN | Videos, imágenes, audio, archivos públicos o estáticos. | "El app server no debe servir blobs grandes; uso Cloud Storage + Cloud CDN." |
| Idempotency-Key | Pagos, órdenes, creación de rides, retries del cliente. | "Para evitar duplicados en retries, acepto un Idempotency-Key por request." |
| Rate limit | APIs públicas, login, short links, feed, scraping. | "Aplicaría rate limit por user/IP/key con token bucket en Redis para protección." |
| ETag / If-Match | Docs, archivos, edición colaborativa, updates concurrentes. | "Para evitar lost updates, uso ETag con If-Match — devuelve 412 si cambió." |
| Cursor pagination | Feeds, búsquedas, listas grandes, historial. | "Uso cursor pagination porque escala mejor que offset en datasets grandes." |
| Field masks | Update parciales, GET con campos selectivos. | "Usaría field mask ?fields=id,name al estilo Google APIs para reducir payload." |
| Observability | Siempre. | "Mediría latencia p50/p95/p99, error rate, saturación y throughput. SLO 99.9%." |
| gRPC / protobuf | Microservicios internos, mobile, streaming. | "Para servicio interno usaría gRPC sobre HTTP/2 — schema fuerte y mejor performance." |
| Circuit breaker | Llamadas a servicios externos o críticos. | "Aplico circuit breaker para evitar cascada de fallos cuando un upstream se degrade." |
| Backoff + jitter | Cualquier retry — cliente o entre servicios. | "Retry con exponential backoff y jitter para no causar thundering herd." |
9 Scripts listos para usar
Aprénde estas frases de memoria. Te ahorran 30 segundos de tartamudeo.
🎬 Apertura: cualquier "diseña X"
"Voy a empezar identificando los recursos principales y las APIs por recurso. Después explico el flujo de escritura y lectura, dónde uso cache o colas, cómo manejo consistencia, seguridad, rate limits y observabilidad. Finalmente reviso tradeoffs y posibles bottlenecks."
🎯 Cierre fuerte (escala)
"El diseño funciona para escala moderada. Para 10× tráfico: separo ruta crítica de procesos asíncronos, agrego cache para lecturas calientes, particiono por userId o region, y mido p95/p99 para detectar bottlenecks. Plan B: read replicas y CDN edge caching."
⚡ Defender una decisión técnica
"Hay un trade-off entre [X] y [Y]. Yo elegiría [X] porque [razón concreta]. Si los requisitos cambian a [escenario], reconsideraría [Y]. Esto se monitorea con [métrica]."
🛡️ Cuando preguntan "¿y si falla?"
"Para failure de [componente]: timeout corto + retry con exponential backoff + circuit breaker. Si persiste, fallback a [degraded mode]. El cliente ve un error claro, no un cuelgue. Alertamos con SLO budget burn."
Pro tip Google: Cuando hables de APIs públicas, menciona que seguirías las Google API Improvement Proposals (AIP.dev): resource-oriented design, standard methods (List, Get, Create, Update, Delete, custom como :cancel), field masks para updates, error model estándar. Esto demuestra que conoces sus estándares internos.