Saltar al contenido

Next.js vs Express: solo uno de los dos renderiza

Express es un servidor sobre el que construyes. Next.js es un framework de renderizado con un servidor incorporado. Elegir entre ambos es elegir cuánto quieres escribir tú.

6 min de lectura

A estos dos se los compara porque los dos son Node y los dos sirven HTTP, que es más o menos donde acaba el parecido. Express es un framework HTTP mínimo que compones tú. Next.js es un framework de renderizado que además incluye un servidor.

La decisión no es realmente una comparación. Es una pregunta sobre qué estás construyendo.

ExpressNext.js
Qué te daUna tubería de peticionesUn camino de renderizado
EnrutadoEl que escribasSistema de archivos, con layouts
RenderizadoNingunoTodo su propósito
Empaquetado del clienteNo es su trabajoIncorporado
Conexiones de larga vidaNativasDe incómodo a imposible
Qué montas túCasi todo un front endCasi todo un backend

Si estás construyendo una API y nada más

Usa Express, o Fastify, o Hono. Los route handlers de Next.js existen y funcionan, pero estarías asumiendo un framework de renderizado, un paso de build, un enrutador basado en sistema de archivos y una dependencia de React para un servicio que no renderiza nada.

En concreto, Express sigue siendo mejor cuando tu API tiene:

  • Consumidores externos. Rutas versionadas, claves de API, contratos documentados.
  • Su propia cadena de middleware. Límite de tasa, validación de peticiones, autenticación a medida, logging que es tuyo.
  • Algo de larga duración. WebSockets, server-sent events, conexiones que sobreviven a una petición.
  • Restricciones de despliegue. Un proceso Node simple es más fácil de operar sobre infraestructura con opiniones.

El punto del middleware merece más que una viñeta, porque es el que se descubre tarde. Una aplicación Express es una tubería que montas: cada petición pasa por las funciones que registraste, en el orden en que las registraste, y puedes razonar sobre toda la cadena leyendo un archivo. Next.js no tiene nada equivalente a nivel de aplicación. proxy.ts - la convención que sustituyó a middleware en la 16 - se ejecuta sobre un matcher, en peticiones que quizá no pretendías, y no es una tubería de peticiones. Intentar usarla como tal es la forma más común en que un equipo traslada una costumbre de Express a una base de código que no puede sostenerla.

Si renderizas páginas, no lo construyas tú

La dirección contraria es más contundente y menos obvia.

Los equipos que empiezan con Express y añaden React encima acaban escribiendo a mano lo que Next.js ya decidió: enrutado, code splitting, obtención de datos en el servidor, streaming, la frontera del bundle de cliente, manejo de imágenes, caché. Cada uno es una semana. Juntos son un framework, y será uno peor, porque es un proyecto paralelo y no el producto.

Si tu aplicación tiene páginas, el argumento a favor de Express es el argumento a favor de escribir tu propio framework. Eso es correcto de vez en cuando y normalmente no.

Lo que se subestima no es ninguno de los puntos de esa lista por separado. Es que esas decisiones interactúan entre sí. El code splitting tiene que saber del enrutado. El streaming tiene que saber de la capa de datos. La frontera de cliente tiene que saber de ambos. Un framework vale lo que vale porque alguien hizo que esas decisiones fueran consistentes entre sí, y un stack propio es donde poco a poco dejan de serlo.

Dónde los route handlers bastan de verdad

Para una API que solo consume tu propio front end, los route handlers de Next.js hacen el trabajo:

// app/api/products/route.ts
export async function GET(request: Request) {
  const products = await db.product.findMany();
  return Response.json(products);
}

Un despliegue, una base de código, tipos compartidos entre la API y los componentes que la llaman. Para la mayoría de las aplicaciones de producto, esta es la cantidad correcta de maquinaria.

Conviene saberlo: para los datos que necesitan tus propias páginas, a menudo no hace falta una ruta. Un Server Component puede consultar directamente, y una Server Action puede mutar; el rodeo por tu propia API HTTP es una costumbre heredada de una arquitectura renderizada en el cliente.

Dónde los route handlers dejan de bastar

Tres límites, en el orden en que los equipos los encuentran.

La conexión tiene que quedarse abierta. Un route handler está construido alrededor de una petición que termina. Los server-sent events son posibles con una respuesta en streaming; un WebSocket no, y en un destino serverless ninguno de los dos sobrevive a la vida de la función. Cualquier cosa con conexión persistente pertenece a un proceso que controles tú.

Algo tiene que ocurrir según un horario. No hay ejecutor de trabajos. Un servicio de cron golpeando un endpoint autenticado cubre los casos simples, y deja de cubrirlos en cuanto un trabajo necesita política de reintentos, cola de mensajes fallidos, o más tiempo del que permite el límite de ejecución de la plataforma.

La API tiene consumidores que no eres tú. Versionado, deprecación, límites de tasa por cliente, una autenticación separada de la sesión de tu front end, y documentación que funcione como contrato. Todo eso se puede construir en route handlers, y todo eso ya existe en el ecosistema que habrías elegido si no.

El arreglo en el que acaban la mayoría de los equipos

Express - o Fastify, o un servicio en Go, o Laravel - sosteniendo la lógica de negocio y los trabajos. Next.js sosteniendo el front end. Hablan por HTTP.

Es la misma conclusión que en Next.js frente a Laravel, por la misma razón: el render path y la lógica de negocio son problemas distintos, y los frameworks son buenos en uno de ellos cada uno.

El coste es real y conviene decirlo: dos destinos de despliegue, un contrato entre ellos que alguien mantiene, y tipos que hay que mantener sincronizados a través de una frontera de red en lugar de comprobados por un único compilador. Los equipos que adoptan esta división sin reconocer ese coste acaban con un front end que reimplementa la mitad de las reglas de la API para evitarse un viaje de ida y vuelta.

Lo que no hay que hacer

Un servidor Express personalizado delante de Next.js. Está soportado y desactiva la optimización estática automática en las rutas que sirve, que es casi todo aquello por lo que viniste. Si necesitas un proxy, ponlo delante de los dos procesos en lugar de dentro de uno.

Portar una cadena de middleware de Express a proxy.ts. La convención que sustituyó a middleware en la 16 corre en Node, no es configurable y se ejecuta en peticiones que no tenías previstas. Es para decisiones de enrutado, no para un pipeline de peticiones.

Preguntas relacionadas

¿Y los WebSockets?
Esa es una de las líneas más claras entre los dos. Una conexión WebSocket sobrevive a la petición que la abrió, y los route handlers de Next.js están construidos alrededor de una petición que termina. En un destino serverless no pueden sostenerla en absoluto. Las conexiones de larga vida van en un proceso que controles tú.
¿No sería Fastify o Hono una comparación mejor que Express?
Para una API nueva, probablemente sí: los dos son más rápidos y tipan mejor. El argumento de esta página no va de Express en particular; va de si quieres un servidor que compones o un framework que ya decidió. Sustituye Express por tu favorito a lo largo del texto.
¿Puede Next.js sustituir del todo nuestra API de Express?
Para una API que solo consume tu propio front end, normalmente sí. Para una con consumidores externos, versionado, límites de tasa y su propia autenticación, estarías reconstruyendo Express dentro de route handlers.
¿Puedo ejecutar Express y Next.js juntos?
Puedes, con un custom server, y cuesta más de lo que parece: un custom server desactiva la optimización estática automática en las rutas que sirve. Ejecutarlos como dos procesos detrás de un proxy inverso suele ser más limpio.

← Volver a todos los artículos

Llamar+1 848 272 7583WhatsApp+90 850 308 5436Correoinfo@codefacture.comPágina de contacto