MongoDB + NodeJS: Tiempo de Respuesta de una API (VPS)

13 minuto(s)

Para esta prueba vamos a hacer uso de un VPS real, para obtener resultados reales en producción

Al menos pensemos en los que están comenzando y que ayude a todos los usuarios en general.

Si tienes un VPS potente o menor que este, puedes hacer las mismas pruebas, solo cambiará el tiempo de respuesta de tu API.

El VPS que usaré tiene las siguientes características de hardware:

  • CPU : 2 vCores
  • RAM : 2GB
  • Disk : 60GB

Vamos a usar una base de datos con 50 registros o datos de productos.

1. Creación de la API

Vamos a realizar pruebas reales como si estuvieramos en producción de las 4 operaciones básicas CRUD (Create, Read, Update y Delete).

1.1 Código de Node JS

Creo un archivo llamado server.js con el siguiente contenido:

1.2 Instalamos las dependencias:

Usaremos Express y Mongoose:

1.3 Crear y poblar la Base de Datos

Ingresamos a la CLI de MongoDB desde la terminal:


Nota: Si tu instalación es un poco más antigua y mongosh te dice “command not found”, intenta con el comando clásico:


Si tu base de datos requiere credenciales (usuario y contraseña), debes conectarte especificando la base de datos de autenticación (que suele ser admin):

Una vez que veas que el prompt de la terminal cambia (generalmente muestra un > o test>), ya estás dentro de MongoDB. Usa estos comandos para explorarla:

En MongoDB, una base de datos se crea automáticamente en el momento en que guardas el primer dato. Vamos a llamarla test_crud y le insertaremos una lista de 50 productos simulados usando un bucle de JavaScript nativo en la consola:


Al presionar Enter, la terminal te devolverá un mensaje confirmando que se insertaron los 50 registros (acknowledged: true junto con los IDs generados), puedes verificar que existan ejecutando: db.productos.countDocuments().

2. Encendiendo el servidor

Para que tu API corra en un entorno de producción real dentro de tu infraestructura, el uso de PM2 es la mejor decisión. PM2 se encargará de mantener la aplicación viva en segundo plano, reiniciarla si el VPS sufre un pico de carga o si el proceso se cae por algún error, y gestionar los logs.

2.1 Instalar PM2 de forma Global (Si no lo tienes)

De paso te sirve para verificar que tengas PM2 instalado globalmente en el Node.js de tu servidor:

2.2 Iniciar la API con PM2

Navega hasta la carpeta del proyecto y enciende el proceso dándole un nombre descriptivo (por ejemplo, api-prod) para que puedas monitorearlo fácilmente:


Nota técnica para ES Modules: Como en el código anterior usamos import en lugar de require, asegúrate de que en el archivo package.json de esa misma carpeta tengas la propiedad “type”: “module”. Si no la tienes, puedes iniciar el proceso forzando a Node a interpretar el archivo correctamente ejecutando: pm2 start app.js –name “api-prod” — node –experimental-modules (o simplemente añadiendo “type”: “module” a tu package.json).

2.3 Comandos de Control para Monitorear el Rendimiento

Una vez encendido, puedes usar estos comandos en la terminal para obtener métricas reales:


Verás una tabla con el nombre api-prod, el ID del proceso, el estado “online”, el uptime, el uso exacto de CPU y cuánta memoria RAM está consumiendo la API en reposo.


Este comando abre una interfaz visual interactiva en la terminal y muestra las gráficas de consumo y los logs en tiempo real.

2.4 Ver los logs de la API


Aquí verás impreso el mensaje de éxito: Servidor API corriendo en puerto 3071 y  Conectado exitosamente a MongoDB local.

¡Listo! Tu API ya está corriendo de forma real y profesional en segundo plano.

3. Pruebas y Benchmarks

Para medir el rendimiento de la API en condiciones 100% reales de producción dentro de tu VPS, la peor forma es recargar la URL en el navegador de tu computadora. El navegador añade latencias de renderizado visual y no genera carga en el servidor.

La forma estándar en la industria es realizar un Test de Estrés y Carga Sintética. Esto simula a cientos de usuarios reales entrando a tu API al mismo tiempo, obligando a Node.js y a MongoDB a procesar datos bajo presión.

Usaré Autocannon, la cual es una herramienta en Node.js capaz de lanzar cargas exigentes. Al ejecutarse mediante hilos de ejecución ligeros, simula de forma muy precisa conexiones concurrentes reales:


Ahora pasemos a realizar las pruebas.

3.1 Obtener los 50 productos (Listar)

Con este comando probamos el endpoint GET, lo que significa que listamos y traemos los 50 productos masivamente desde MongoDB bajo una carga intensa de usuarios simulados:


Explicación de los parámetros:

  • -c 100: Simula 100 usuarios concurrentes haciendo peticiones al mismo tiempo de forma ininterrumpida.
  • -d 10: Mantiene el test corriendo de forma intensa durante 10 segundos.

Y obtenemos la siguiente respuesta:

Prueba de listados de productos
Simulando exactamente a usuarios concurrentes (al mismo tiempo)

Analizando los resultados:

  • Peticiones por Segundo (Avg 173.3 Req/Sec): El servidor procesó e imprimió con éxito 1,733 peticiones en total en esos 10 segundos. Significa que la API es capaz de escupir la lista de productos unas 173 veces por segundo de manera sostenida bajo estrés. ¡Es un número muy sólido para empezar!
  • La Latencia (Avg 565.03 ms): Aquí vemos el costo del esfuerzo. En promedio, cada usuario tuvo que esperar poco más de medio segundo (565 milisegundos) para recibir la respuesta. El punto máximo de congestión llegó a 1662 ms (1.6 segundos). No es terrible para estar bajo un ataque de 100 conexiones concurrentes, pero nos dice que el hilo único (Single Thread) de Node.js o los tiempos de respuesta de la base de datos se empezaron a acumular en la cola de espera.

Un detalle excelente: se leyó 15.2 MB en total y la desviación estándar (Stdev de 29.82) es baja, lo que significa que el servidor se mantuvo estable. No tuvo caídas drásticas de rendimiento ni picos raros.

3.2 Crear productos masivamente (POST)

Con este comando probamos el endpoint POST, lo que significa que simulamos la creación masiva de nuevos registros, obligando a la API de Express a procesar datos entrantes y a MongoDB a escribir en el disco bajo una carga intensa:


Explicación de los parámetros:

  • -c 50: Simula 50 usuarios concurrentes enviando peticiones de creación de manera simultánea e ininterrumpida.
  • -d 5: Mantiene el test corriendo de forma intensa durante 5 segundos (reducimos el tiempo ya que las operaciones de escritura son más pesadas para el disco).
  • -m POST: Especifica que el método HTTP a utilizar será POST en lugar del GET por defecto.
  • -H “Content-Type: application/json”: Agrega la cabecera necesaria para indicarle a nuestra API que le estamos enviando datos en formato JSON nativo.
  • -b “{\”nombre\”:\”Producto de Carga\”,\”precio\”:25}”: Define el cuerpo (body) de la petición con la estructura del objeto que la base de datos necesita insertar.

Y obtenemos la siguiente respuesta:

Crear productos masivamente (POST)
Al no ver la línea de non 2xx responses, significa que todas y cada una de las peticiones fueron procesadas de forma correcta por Express e insertadas en MongoDB.

Analizando los resultados:

  • Peticiones por Segundo (Avg 624.8 Req/Sec): La API logró procesar y meter en la base de datos más de 3,000 productos nuevos en solo 5 segundos. Mantener más de 620 inserciones por segundo en un entorno de un solo hilo (Node.js) con persistencia en disco es un rendimiento fantástico para el VPS.
  • La Latencia Real de Escritura (Avg 79.39 ms): Ahora que Express sí tiene que parsear el JSON, Mongoose debe validar el esquema y MongoDB tiene que escribir el registro en el almacenamiento del VPS, el tiempo de respuesta subió a 79.39 milisegundos en promedio.
  • Comportamiento en Picos Intensos (Max 196 ms): Incluso en el peor momento del bombardeo con 50 conexiones simultáneas sin pausa, la respuesta más lenta tardó apenas 196 ms (menos de 0.2 segundos). Esto garantiza que la API no se va a colgar ni va a hacer esperar al usuario final bajo ráfagas altas de tráfico.

3.3 Obtener y listar los productos (GET)

Ahora probemos el endpoint GET, lo que significa que realizamos consultas masivas simultáneas para traer el listado completo de productos desde la base de datos de MongoDB bajo una carga intensa de usuarios simulados:


Explicación de los parámetros:

  • -c 100: Simula 100 usuarios concurrentes haciendo peticiones al mismo tiempo de forma ininterrumpida y sin tiempos de espera entre cada consulta.
  • -d 10: Mantiene el test corriendo de forma intensa durante 10 segundos continuos para evaluar la estabilidad del bucle de eventos (Event Loop) de Node.js.

Y obtenemos la siguiente respuesta:

Obtener y listar los productos (GET)
Paso de 173 Req/Sec a solo 2.8 Req/Sec, y la latencia promedio se disparó a casi 7 segundos (6865.65 ms), acumulando 72 errores por timeout

Analizando los resultados:

¿Por qué colapsó el endpoint GET al meterle los miles de productos?

El culpable está en tu archivo server.js, específicamente en esta línea:


Al no tener paginación, cada una de las 100 conexiones simultáneas le pidió a Express que fuera a MongoDB, trajera los miles de productos creados en el test anterior de golpe, los ordenara por fecha, cargara ese JSON masivo en la memoria RAM de Node.js y lo enviara por la red.

Como el canal de red y el hilo único de Node.js se saturaron transfiriendo archivos JSON gigantescos (¡leyó 20.9 MB con apenas 200 peticiones!), el servidor empezó a dejar peticiones en cola hasta que Autocannon se cansó de esperar y arrojó los timeouts.

El peligro de las consultas masivas sin paginación (Análisis del colapso)

Tras ejecutar el test de estrés POST e inyectar miles de registros en MongoDB, volvimos a lanzar la prueba sobre el endpoint de lectura (GET). El resultado demostró una degradación crítica del rendimiento debido a la falta de límites en la consulta:

  • Caída de Req/Sec: El rendimiento cayó drásticamente a un promedio de 2.8 Req/Sec debido a la enorme transferencia de datos en memoria.
  • Latencia Crítica: El tiempo de respuesta promedio subió a 6,865.65 ms, provocando que el servidor dejara de responder a tiempo y generando 72 errores de timeout.
  • Saturación de Red: Con apenas 200 peticiones completadas, el servidor tuvo que mover 20.9 MB de información, lo que demuestra que transferir colecciones completas sin paginar rompe la escalabilidad del backend.

Solución: Limpieza de la Colección

Para continuar con nuestras pruebas CRUD de manera controlada y mantener los 50 productos base estipulados para el artículo, procederemos a vaciar los registros basura generados por el test de estrés anterior y a reestablecer nuestro listado inicial.

Si estás usando la consola de MongoDB (mongosh), puedes vaciar la colección rápidamente con el siguiente comando:


Nota: Tras vaciarla, volví a ejecutar el script de inserción inicial o semilla para restaurar únicamente los 50 productos de prueba.

Repitiendo el Test de Lectura (GET) con 50 Productos

Una vez que restablecimos la base de datos a su estado limpio con exactamente 50 documentos, volvimos a lanzar el test de estrés con Autocannon para medir el rendimiento real de la API en un entorno controlado:


Y obtenemos la siguiente respuesta:

Repitiendo el Test de Lectura (GET) con 50 Productos
¡Ahora sí! Qué diferencia en los números. Al limpiar la base de datos y dejar exactamente los 50 productos optimizados para el esquema, el rendimiento volvió a equilibrarse por completo.

Analizando los resultados:

  • Estabilización del Rendimiento (Avg 165.3 Req/Sec): Al limitar el peso del payload (los datos transmitidos), el hilo único de Node.js pudo resolver más de 2,000 peticiones en 10 segundos, manteniendo el servidor fluido bajo el bombardeo de 100 usuarios simultáneos.
  • Latencia Controlada (Avg 485.89 ms): Aunque el 50% de las peticiones se resolvieron en apenas 294 ms (percentil 50%), vemos que bajo estrés máximo al final del test la latencia llegó a subir (Stdev de 1,080 ms) y se generaron 16 timeouts. Esto es genial, porque demuestra que incluso con 50 registros, meter a 100 usuarios concurrentes sin un sistema de caché (como Redis) o paginación estricta empieza a exigirle el máximo al droplet.

3.4 Actualizar productos de forma masiva (PUT)

Con este comando probamos el endpoint PUT, simulando que múltiples usuarios intentan actualizar los datos de un producto de manera concurrente, forzando a la API a procesar modificaciones en la base de datos:


Explicación de los parámetros:

  • -m PUT: Especifica que utilizaremos el método HTTP PUT para realizar la actualización del recurso.
  • -b “…”: Envía el objeto JSON con las modificaciones deseadas debidamente escapado para Bash.

Y obtenemos la siguiente respuesta:

Actualizar productos de forma masiva
Se proceso cerca de 2,000 peticiones exitosas en solo 5 segundos

Analizando los resultados:

  • Velocidad de Procesamiento (Avg 338.4 Req/Sec): Mantener un promedio de casi 340 actualizaciones por segundo en un solo registro bajo el bombardeo de 50 conexiones simultáneas es un rendimiento excelente. Esto demuestra que findByIdAndUpdate() responde de forma sumamente ágil cuando trabaja sobre un único índice directo (el _id).
  • Latencia Muy Fluida (Avg 145.56 ms): El tiempo medio que tardó el servidor en recibir el JSON, conectarse a MongoDB, aplicar los cambios en el documento, guardar en disco y retornar la respuesta fue de apenas 145.56 milisegundos. Es una respuesta sumamente rápida para operaciones que modifican el estado de la base de datos.
  • Consistencia Máxima (Max 315 ms): Incluso en el pico más alto de estrés de la prueba, la petición más lenta se resolvió en solo 315 ms (menos de un tercio de segundo), manteniendo una desviación estándar bajísima de 42.71 ms. Esto significa que el rendimiento del servidor es predecible y no sufre de caídas o picos extraños durante ráfagas de edición de datos.

3.5 Eliminar productos de forma masiva (DELETE)

Con este comando probamos el endpoint DELETE, simulando un escenario donde múltiples conexiones simultáneas intentan dar de baja un mismo recurso específico en el sistema:


Explicación de los parámetros:

  • -m DELETE: Especifica que el método HTTP a utilizar será DELETE, indicándole a Express que la instrucción es remover permanentemente el registro asociado al ID de la URL.
  • …/productos/6a4f0446fa54fe81f68563b1: Apunta directamente al identificador único del producto que deseamos eliminar, forzando al backend a ejecutar la acción findByIdAndDelete().

Y obtenemos la siguiente respuesta:

Eliminar productos de forma masiva (DELETE)
Obtenemos 1 2xx responses, 3679 non 2xx responses

Analizando los resultados:

  • Peticiones por Segundo Máximas (Avg 736 Req/Sec): Al no tener que escribir datos complejos en disco de forma repetitiva, el rendimiento subió hasta las 736 peticiones por segundo.
  • Latencia Ultra Baja (Avg 67.26 ms): El tiempo de respuesta promedio fue bajísimo comparado con el PUT y el POST. Responder que un recurso ya no existe es una operación sumamente ligera para Express, lo que mantuvo el servidor muy relajado con un pico máximo de apenas 178 ms.

4. Consejos prácticos para optimizar tu API Express y MongoDB

Basándonos en el comportamiento real que observamos durante los tests de estrés, aquí tienes las mejores prácticas que todo desarrollador debe implementar antes de llevar una API de este tipo a producción:

  • Implementa Paginación Obligatoria en Lecturas: El colapso del endpoint GET (donde la latencia subió a casi 7 segundos) demostró que transferir colecciones completas rompe el servidor. Usa siempre .skip() y .limit() en Mongoose para segmentar las respuestas en bloques pequeños (por ejemplo, de 20 o 50 registros).
  • Aprovecha el Poder de los Índices: Las pruebas PUT y DELETE fueron sumamente rápidas (bajando a ~67 ms en la eliminación) porque MongoDB busca de forma nativa e instantánea a través del índice automático _id. Si vas a buscar productos por campos como sku o categoria, asegúrate de definirlos como índices (index: true) en tu esquema de Mongoose.
  • Maneja la Validación en el Cliente y en el Backend: Como vimos en el test POST, omitir campos obligatorios del esquema bloquea la persistencia y genera respuestas de error. Asegúrate de validar los datos entrantes en una capa intermedia (middleware) antes de tocar la base de datos para liberar de carga a MongoDB.
  • Considera un Sistema de Caché (como Redis): Si tu endpoint de lectura de productos va a recibir cientos de conexiones concurrentes para mostrar siempre los mismos datos, implementar una capa de caché evitará que Node.js tenga que consultar a MongoDB en cada petición, reduciendo drásticamente los timeouts.

5. Conclusión

Realizar pruebas de estrés con Autocannon nos permite quitar la venda de los ojos y entender cómo se comportará nuestra infraestructura (Node.js, Express y MongoDB) bajo escenarios de tráfico masivo en el mundo real.

Node.js, gracias a su arquitectura basada en un bucle de eventos (Event Loop) no bloqueante, demuestra una capacidad sobresaliente para despachar miles de peticiones HTTP por segundo de manera eficiente. Sin embargo, el verdadero reto no está en el servidor web, sino en la estrategia de persistencia y transferencia de datos. Operaciones de escritura atómicas (POST, PUT) o respuestas ligeras de error rindieron con total fluidez, mientras que las consultas masivas sin control (GET) evidenciaron la necesidad crítica de diseñar código defensivo y optimizado desde el primer día.

Lanzar una API sin antes medir sus límites es una apuesta arriesgada, herramientas ligeras y potentes como Autocannon nos devuelven el control total sobre la escalabilidad de nuestras aplicaciones.

MongoDB 08-07-2026 08-07-2026 Crear un PostEventos DevsForo

Sobre el Autor

Juan Castro

Juan Castro — Ingeniero de Software con más de 17 años de experiencia en desarrollo, ia, ml, devops, data science, ciberseguridad y tecnología.

Certificados oficiales:
Certificado Microsoft Azure Fundamentals Certificado AWS Designing Event-Driven Architectures Certificado Google Desarrollo de Aplicaciones Móviles

Ver más

IA