Mejores prácticas de configuración de Next.js
Un resumen condensado de las 25 mejores prácticas más importantes extraídas de cada página de esta sección.
- Genera un AUTH_SECRET Real: Auth.js requiere una
AUTH_SECRETde 32+ caracteres; usanpx auth secretpara obtener un valor criptográficamente fuerte, y da secrets diferentes para dev y prod para que una key dev filtrada no comprometa sesiones reales. - Separa auth.config de auth.ts: Mantén un
auth.config.ts(edge-safe) (sin adaptadores, sin drivers de BD) separado deauth.tsdonde conectas el adaptador de base de datos, para que middleware pueda llamar aauth()bajo el Edge Runtime sin arrastrar código Node-only. - Aumenta el tipo Session: Declara claims personalizadas en
SessionyJWTentypes/next-auth.d.tsvía module augmentation para quesession.user.role,subscriptionStatus, etc. estén tipadas en cada Server Component, action y middleware. - Fija create-next-app para Reproducibilidad: Los defaults de CLI varían entre versiones y el modo interactivo silenciosamente reutiliza preferencias cacheadas, así que fija
create-next-app@15(o la major que quieras) y pasa--reset-preferenceso flags explícitos en CI para que los scaffolds sean determinísticos. - Cachea fetch Explícitamente en Next 15: El default en Next.js 15+ es
cache: "auto", noforce-cache, así que establececacheonext: { revalidate: N }en cada fetch en lugar de asumir que los datos están cacheados — el fetching silencioso por-solicitud es una regresión común respecto a Next 14. - Copia static y public a standalone:
output: "standalone"produce un servidor diminuto autónomo pero excluye.next/staticypublic/— tu Dockerfile o script de despliegue debe copiar ambos, sino la app sirve 404s para assets. - Establece HOSTNAME=0.0.0.0 en Contenedores: El servidor standalone se vincula a
127.0.0.1por default, así que dentro de un contenedor Fargate/EKS/Docker debes establecerENV HOSTNAME=0.0.0.0(y unPORTexplícito) o el health check del load balancer nunca alcanza la app. - Usa un Cache Handler ISR Compartido: Los datos ISR se cachean por-contenedor por default, así que despliegues multi-contenedor muestran versiones inconsistentes hasta que configures un
cacheHandlerrespaldado por Redis ennext.configconcacheMaxMemorySize: 0. - Prefiere pm2 reload Sobre restart: En EC2,
pm2 reloadhace un graceful rolling restart en cluster workers para despliegues zero-downtime;pm2 restartmata todos los workers y produce una ventana de outage visible. - Deja que Nginx Sirva _next/static: Configura Nginx para servir
/_next/static/ypublic/directamente conCache-Control: public, immutable, max-age=31536000para que los assets estáticos eviten Node completamente y sobrevivan reinicios de app. - Preserva .next/cache Entre Despliegues: Los scripts de despliegue deben eliminar
.next/server/y.next/static/pero guardar.next/cache/para que los cachés ISR y de compilación sobrevivan; un bluntrm -rf .nextlos borra y fuerza reconstrucciones completas más regeneración ISR fría. - Usa un Cliente Prisma Singleton: Adjunta el cliente a
globalThisen development para que hot-reload no abra un nuevo connection pool en cada guardado — sin esto, Postgres rechaza nuevas conexiones en minutos y el dev server se cuelga. - Pon DATABASE_URL en .env: El Prisma CLI lee
.env, no.env.local, así queprisma generateymigratefallan silenciosamente o con mensajes confusos si la URL solo vive en.env.local. - revalidatePath Después de Mutaciones: Server Actions que escriben datos deben llamar a
revalidatePath(orevalidateTag) después, sino el render cacheado de Server Component sigue sirviendo datos obsoletos y la UI parece no actualizar. - Usa prisma migrate deploy en CI: Los pipelines de CI deben ejecutar
prisma migrate deploy(no-interactivo, aplica migraciones confirmadas);prisma migrate deves interactivo, puede resetear la base de datos, y nunca debe ejecutarse contra production o staging. - Valida Frontmatter MDX Con Zod:
gray-matterdevuelve las keys que happen estar en el archivo, así que ejecuta el resultado a través de un schema Zod para atrapar typos y campos requeridos faltantes en tiempo de importación en lugar de hacer ship de docs rotas. - Escapa Braces y JSX en MDX Prose: Las llaves curvas se evalúan como JavaScript y
<Tag />se parsea como JSX, así que envuelve ejemplos de código en backticks o escapa las braces — sino MDX lanza build errors en prose que se ve bien en plain Markdown. - Crea Clientes Stripe en Signup: Crea el cliente Stripe en Auth.js
events.createUser, no durante checkout, para que no haya double-click race que produzca clientes duplicados y el usuario tenga un Stripe ID en el momento que existan. - Verifica Webhooks Stripe Con constructEvent: Un webhook handler de Stripe debe leer el cuerpo raw y llamar a
stripe.webhooks.constructEvent(rawBody, sig, whsec); parsear JSON sin verificar la signature deja que cualquiera mute tu base de datos vía POSTs forjados. - Fuerza nodejs Runtime para Webhooks: Establece
export const runtime = "nodejs"en la ruta webhook de Stripe porquecrypto.createHmacno está disponible en el Edge Runtime, y usa unSTRIPE_WEBHOOK_SECRETdistinto por entorno o las signatures fallan silenciosamente. - Tailwind v4 Config Vive en CSS: Tailwind v4 no tiene
tailwind.config.js— los tokens de tema, variantes y plugins se declaran englobals.cssvía@themey@custom-variant, y el plugin PostCSS es@tailwindcss/postcss, no el viejo paquetetailwindcss. - Siempre Compone Classes Con cn(): Los componentes shadcn colapsan utilidades en conflicto vía
cn()(clsx+tailwind-merge), así que concatena classes a través decn(...)en lugar de template strings — sino los overrides de prop de consumer aleatoriamente pierden contra defaults. - Declara env Vars en turbo.json: Turborepo despoja cualquier variable de entorno no listada en el array
envde la tarea, así que vars no-listadas sonundefineden build time; declara cada env var de build time explícitamente o cache keys no reflejarán tu configuration. - transpilePackages para Workspace UI: Next.js no compilará TSX raw desde
node_modules, así que cuando consumes paquetes monorepo internos como@repo/uiañádelos atranspilePackagesennext.config.tso imports erroan en build. - Usa workspace: Con pnpm:* El protocolo
workspace:*solo lo entienden pnpm, yarn, y bun — npm no lo parsea; establece"packageManager": "pnpm@…"en rootpackage.jsony usa--frozen-lockfileen CI para evitar drift.