Desarrolla una API GraphQL profesional con Node.js, Apollo y Prisma

Desarrolla una API GraphQL profesional con Node.js, Apollo y Prisma

Tutorial GraphQL Node.js TypeScript: API de producción con Apollo Server, Prisma y PostgreSQL

Aprende a construir una API GraphQL de producción con Node.js 18+, Apollo Server, TypeScript, Prisma y PostgreSQL, usando buenas prácticas de seguridad, rendimiento y DX. Este tutorial está pensado para desarrolladores backend principiante-intermedio que conocen JavaScript/TypeScript y HTTP/REST, y quieren dar el salto a GraphQL en un entorno real.

Diagrama del flujo de datos:


1) Requisitos e instalación (comprobación rápida)

Resumen: Instalaremos y verificaremos Node 18+, gestor de paquetes, Git, Docker, Docker Compose, PostgreSQL y Prisma CLI.

Pasos:

  • Asegúrate de tener Node 18+ y un gestor de paquetes (pnpm recomendado). También necesitarás Docker y Git.
  • Opcional: protoc si vas a añadir codegen en el futuro.

Comandos de verificación:

Explicación: Validamos que las herramientas clave estén instaladas. Prisma se ejecuta vía npx, por lo que no es obligatorio instalarlo globalmente.

Checklist de la sección:

  • [ ] Node 18+ instalado y verificado
  • [ ] pnpm/npm/yarn verificado
  • [ ] Docker y Docker Compose listos
  • [ ] psql accesible (o usarás Docker para la DB)
  • [ ] Prisma CLI accesible con npx

2) Inicialización del proyecto y configuración base

Resumen: Crearemos el repositorio, la estructura de carpetas, TypeScript, scripts NPM y herramientas de calidad (ESLint/Prettier). Usaremos pnpm.

Pasos:

Explicación: Instalamos dependencias para Apollo Server v4 con Express, autenticación JWT, validación con Zod, DataLoader, logging, métricas, WebSockets para subscriptions, Redis opcional, Prisma y herramientas de testing/lint.

Estructura recomendada del repo:

Explicación: Estructuramos por dominios y separamos resolvers y utilidades para escalabilidad.

Archivo package.json (scripts clave):

Explicación: Scripts para desarrollo, build, migraciones y seed. Ajusta devDependencies y dependencies automáticamente con pnpm.

tsconfig.json mínimo:

Explicación: Configuración estricta de TypeScript para Node 18.

Opcional: Husky + lint-staged

Explicación: Añade pre-commits automáticos para calidad de código.

Checklist:

  • [ ] Dependencias instaladas
  • [ ] Scripts npm listos
  • [ ] tsconfig configurado
  • [ ] Husky/lint-staged opcional

3) Diseño del esquema GraphQL (SDL) para producción

Resumen: Usaremos enfoque schema-first con SDL en schema.graphql, incorporando tipos, entradas, enums y paginación cursor-based estilo Relay.

Diferencias: schema-first (SDL) te da contrato explícito y revisión independiente; code-first genera SDL desde código. Elegimos SDL por claridad y portabilidad.

Archivo src/schema.graphql:

Explicación: Definimos entidades, conexiones con edges para paginación y operaciones. Las suscripciones emitirán nuevos posts.

Checklist:

  • [ ] SDL creado con tipos y paginación cursor-based
  • [ ] Queries/Mutations/Subscriptions separadas
  • [ ] Inputs validados en resolvers

4) Prisma + PostgreSQL (modelado, migraciones y seed)

Resumen: Modelaremos User, Post y Comment con relaciones y timestamps. Ejecutaremos migraciones y un seed básico.

prisma/schema.prisma:

Explicación: Modelos con relaciones 1-N, timestamps y soft delete opcional vía deletedAt.

Archivo .env.example:

Explicación: Variables para servidor, JWT, DB y métricas. Copia a .env.

Seed (prisma/seed.ts):

Explicación: Crea un usuario admin y un post de ejemplo.

Comandos:

Explicación: Genera el Prisma Client, aplica migraciones y siembra datos.

Checklist:

  • [ ] Modelos definidos
  • [ ] Migraciones aplicadas
  • [ ] Seed ejecutado

5) Contexto, autenticación y autorización (JWT + roles)

Resumen: Implementaremos login/register con JWT, extracción de usuario desde Authorization y checks de autorización por rol/propiedad.

src/prisma.ts:

Explicación: Singleton de Prisma para evitar múltiples conexiones en dev.

src/auth/jwt.ts:

Explicación: Firma y verificación de tokens JWT de acceso y refresh.

src/auth/auth.middleware.ts:

Explicación: Extrae el usuario actual desde el header Authorization Bearer.

src/context.ts:

Explicación: Inyecta Prisma, DataLoaders, usuario actual y logger en context.

Autorización utilitaria (src/modules/users/resolvers.ts ejemplificará owner/admin checks). Veremos los guards en resolvers de Mutations.

Checklist:

  • [ ] JWT creado con access/refresh
  • [ ] Usuario inyectado en context
  • [ ] Guards listos en resolvers

6) Resolvers, DataLoader y batching (anti N+1)

Resumen: Implementaremos resolvers por dominio y DataLoader para agrupar consultas N+1 como Post.author y Comment.author.

src/loaders/dataloader.ts:

Explicación: DataLoader batch por IDs de usuario. Mejora rendimiento resolviendo N+1.

src/utils/sanitize.ts:

Explicación: Sanitiza strings para prevenir XSS en campos renderizados.

src/resolvers/query.ts:

Explicación: Implementa me, user y posts con paginación cursor-based y límite de página.

src/resolvers/user.ts y post.ts (resolvers de campos):

Explicación: Resolver de campos que usa DataLoader para author y aplica paginación en posts y comentarios.

src/resolvers/mutation.ts:

Explicación: Validamos inputs con Zod, sanitizamos strings, aplicamos autenticación y publicamos eventos para subscriptions.

src/resolvers/subscription.ts:

Explicación: Suscripción a nuevos posts usando un PubSub simple (veremos su inyección en index.ts).

Checklist:

  • [ ] DataLoader implementado
  • [ ] Resolvers organizados por dominio
  • [ ] Cache del loader invalidada en mutaciones relevantes

7) Subscriptions y tiempo real (graphql-ws)

Resumen: Configuraremos graphql-ws con ws. Para escalar, podrás usar Redis Pub/Sub. Localmente usaremos PubSub en memoria.

src/index.ts (servidor Express + Apollo + WS + métricas):

Importante: Asegúrate de tener instalados los imports mencionados en package.json.

Explicación: Montamos Apollo con Express, habilitamos seguridad, métricas y levantamos un servidor WS en la misma ruta para subscriptions con graphql-ws.

Probar suscripciones localmente (node script rápido):

Explicación: Cliente minimal de graphql-ws que imprime eventos onNewPost.

Checklist:

  • [ ] WS server activo
  • [ ] PubSub inyectado
  • [ ] Cliente de prueba funcionando

8) Caching y rendimiento (Redis, cache control, límites)

Resumen: Añadiremos cache de respuesta y un ejemplo simple con Redis para queries costosas y invalidación tras mutaciones.

Ejemplo de cache en resolver con Redis (opcional):

Explicación: Cache genérico con ioredis e invalidación básica.

Uso en Query.posts:

Explicación: Cachea listas por 30 segundos; invalida tras createPost si aplica.

Checklist:

  • [ ] Cache de respuesta habilitado
  • [ ] Redis opcional integrado
  • [ ] Límites de page size y ordenación establecidos

9) Uploads y archivos (URLs firmadas S3, seguro)

Resumen: Recomendado usar URLs firmadas con S3 para evitar transportar binarios por GraphQL. Creamos una mutation que devuelve una URL firmada.

Schema (añadir):

Resolver (pseudo-implementación con AWS SDK v3):

Explicación: Devuelve URL firmada temporal; el cliente sube directo a S3.

Checklist:

  • [ ] Evitar subir binarios por GraphQL
  • [ ] Validar content-type y tamaño en el cliente/servidor

10) Testing y calidad (Jest + E2E)

Resumen: Añadiremos Jest y una prueba E2E simple contra el endpoint GraphQL.

jest.config.ts:

Explicación: Configuración mínima para TypeScript.

tests/example.e2e.ts:

Explicación: Prueba de extremo a extremo: login y consulta de posts autenticada.

Servidor de prueba (src/test-server.ts):

Explicación: Servidor simplificado para pruebas, sin WS ni plugins.

Checklist:

  • [ ] Jest configurado
  • [ ] Pruebas E2E mínimas
  • [ ] Seed ejecutado antes de tests (si aplica)

11) Observabilidad y métricas (Pino, OpenTelemetry, Prometheus)

Resumen: Ya incluimos Pino y Prometheus. Opcionalmente añade OpenTelemetry para tracing distribuido.

  • Pino: usado en context para logs estructurados.
  • Prometheus: endpoint /metrics con prom-client.
  • OpenTelemetry: integrar @opentelemetry/api y SDK es opcional por brevedad.

Checklist:

  • [ ] Logs estructurados
  • [ ] /metrics expuesto
  • [ ] Health/readiness endpoints activos

12) Dockerización y orquestación local

Resumen: Crearemos Dockerfile multi-stage y docker-compose con API, Postgres y Redis.

Dockerfile:

Explicación: Compila en una etapa y ejecuta en otra, con Prisma Client generada y schema.graphql copiado.

docker-compose.yml:

Explicación: Compose con servicios DB, Redis y API. La API aplica migraciones en arranque.

Comandos:

Explicación: Levanta toda la pila localmente.

Checklist:

  • [ ] Imagen multi-stage creada
  • [ ] Compose con DB y Redis
  • [ ] Migraciones en arranque

13) CI/CD básico (GitHub Actions + Docker)

Resumen: Pipeline con install, lint, test, build, push de imagen y migrate deploy en entorno de staging/prod.

.github/workflows/ci.yml:

Explicación: Ejecuta tests con Postgres de servicio, luego construye y publica la imagen en GHCR.

Checklist:

  • [ ] Workflow con tests/migraciones
  • [ ] Build y push de imagen

14) Seguridad y mejores prácticas (rate limit, CORS, complexity)

Resumen: Añadimos rate limit, CORS seguro, helmet y límite de complejidad para GraphQL.

Límite de complejidad (ejemplo rápido):

Explicación: Para producción usa graphql-query-complexity como plugin en la ejecución para rechazar consultas demasiado costosas.

Otras recomendaciones:

  • Deshabilita introspection en prod si usas consultas persistentes.
  • Usa persisted queries/CDN para cachear GETs.
  • Rota claves JWT periódicamente.

Checklist:

  • [ ] Rate limiting activo
  • [ ] CORS restringido
  • [ ] Complejidad/depth limitado

15) Cómo probar: curl/HTTPie y ejemplos GraphQL

Resumen: Ejecuta la API y prueba queries, mutations y subscriptions desde terminal.

Arranque local:

Queries/mutations:

Explicación: Ejemplos con curl/HTTPie para autenticar, consultar y crear.

Subscription (cliente script ya mostrado) o con wscat enviando JSON con protocolo graphql-ws.

Checklist:

  • [ ] Puedes autenticarte y consultar datos
  • [ ] Subscription recibe eventos al crear posts

16) Despliegue y escalado

Resumen: Consideraciones para producción y alta disponibilidad.

  • Stateless servers: escala varias réplicas; usa Redis para PubSub en subscriptions (graphql-redis-subscriptions).
  • Prisma en serverless: usa Prisma Accelerate/Data Proxy para pools eficientes.
  • Pool sizing: ajusta conexiones según DB (p.ej. 10–20 por instancia).
  • Balanceo y health probes: usa /healthz y /readyz.
  • CDN y persisted queries: mejoran latencia y cacheabilidad.

Checklist:

  • [ ] Servidores sin estado
  • [ ] PubSub compartido (Redis) si usas subscriptions
  • [ ] Conexiones de DB dimensionadas

17) Resolución de problemas comunes

Resumen: Errores típicos y soluciones rápidas.

  • Prisma migrate falla en CI: asegura npx prisma generate y migrate deploy antes de iniciar server; revisa DATABASE_URL.
  • CORS bloquea Playground/WS: ajusta CORS_ORIGIN y verifica que WS usa la misma ruta /graphql y host permitido.
  • JWT expirado: captura GraphQLError, refresca con refreshToken mutation y reintenta.
  • N+1 problem: confirma que resolvers de campos usan DataLoader.
  • Prisma Client no generado en CI: ejecuta npx prisma generate tras pnpm install.
  • Docker no conecta a DB: usa el host del servicio (db) y la red de Compose; no uses localhost dentro del contenedor.
  • Queries lentas: añade índices (ya tenemos en authorId/postId), limita page size y usa EXPLAIN en consultas pesadas.

18) README (pasos rápidos para clonar y correr)

README sugerido:

Explicación: Guía mínima para nuevos contribuidores.


19) FAQ (respuestas cortas)

  • ¿GraphQL es más lento que REST? No necesariamente; con DataLoader, paginación y cache, puedes igualar o mejorar.
  • ¿Cómo protejo la API? Rate limiting, CORS, helmet, límites de complejidad y JWT sólidos con rotación.
  • ¿Puedo usar subscriptions en producción? Sí, con Redis PubSub o Kafka y servidores sin estado.
  • ¿Prisma soporta transacciones? Sí, usa prisma.$transaction.
  • ¿Cómo hago uploads? Preferible URLs firmadas a un bucket (S3, GCS).

Meta title, description y slug

  • Meta title: Tutorial GraphQL Node.js TypeScript: Apollo Server, Prisma y PostgreSQL en producción
  • Meta description: Guía paso a paso para crear una API GraphQL de producción con Node.js 18+, Apollo Server, TypeScript, Prisma y PostgreSQL. Incluye JWT, DataLoader, subscriptions, Docker y CI/CD.
  • URL slug: tutorial-graphql-nodejs-typescript-apollo-prisma-postgresql

Checklist global para producción

  • [ ] Variables de entorno seguras (JWT secrets, DATABASE_URL, CORS)
  • [ ] Migraciones aplicadas y seed opcional ejecutado
  • [ ] Logs estructurados y métricas /metrics disponibles
  • [ ] Rate limiting, helmet y CORS configurados
  • [ ] Límite de complejidad/depth en GraphQL
  • [ ] DataLoader implementado y probado (sin N+1)
  • [ ] Subscriptions con Redis si se escala horizontalmente
  • [ ] Dockerfile multi-stage y compose listos
  • [ ] CI con tests, build y push de imagen
  • [ ] Monitorización de salud (/healthz, /readyz) y alertas