¿Qué es MERN?
Qué es la pila MERN: arquitectura de tres capas, el papel de MongoDB, Express, React y Node.js, el recorrido completo de una petición, JSON como formato de intercambio, comparación con otras pilas y criterios para elegirla.
Definición
MERN es una pila (stack) de cuatro tecnologías de código abierto para construir aplicaciones web completas con un solo lenguaje, JavaScript:
| Letra | Tecnología | Qué es | Capa |
|---|---|---|---|
| M | MongoDB | Base de datos de documentos (NoSQL) | Datos |
| E | Express | Framework minimalista para servidores HTTP | Lógica (backend) |
| R | React | Biblioteca para construir interfaces de usuario | Presentación (frontend) |
| N | Node.js | Entorno de ejecución de JavaScript fuera del navegador | Lógica (backend) |
Una aplicación full stack abarca las tres capas: lo que el usuario ve, el servidor que aplica las reglas del negocio y el lugar donde se guardan los datos. MERN cubre las tres sin cambiar de lenguaje, y los datos viajan entre ellas en el mismo formato: JSON.
Arquitectura de tres capas
MERN sigue el modelo cliente-servidor en tres capas (three-tier architecture). Cada capa tiene una responsabilidad y solo habla con la vecina:
| Capa | Tecnología | Responsabilidad | Dónde corre |
|---|---|---|---|
| Presentación | React | Dibujar la interfaz, reaccionar a clics y formularios, pedir datos a la API | Navegador del usuario |
| Lógica | Node.js + Express | Recibir peticiones HTTP, validar, autenticar, aplicar reglas del negocio y responder | Servidor (Render, un VPS, un contenedor) |
| Datos | MongoDB | Guardar y consultar documentos de forma persistente | Servidor de base de datos (MongoDB Atlas) |
Dos consecuencias prácticas:
- El navegador nunca habla con la base de datos. Todo pasa por la API de Express, que es la única que conoce las credenciales de MongoDB. Si React se conectara directo, cualquiera podría leer la contraseña desde el navegador.
- Cada capa se puede reemplazar o escalar por separado. El frontend se publica como archivos estáticos (Vercel), la API como un servicio (Render) y la base como un clúster administrado (Atlas). Es exactamente como publicaremos el proyecto en el módulo 7.
El recorrido de un pedido
Así viaja un pedido de la taquería, de la pantalla a la base de datos y de regreso:
- React arma el pedido con lo que el cliente eligió y lo envía con
fetch: métodoPOST, ruta/api/pedidos, cuerpo en JSON. - La petición HTTP viaja por internet hasta el servidor.
- Node.js recibe la conexión y Express la dirige a la función que atiende
POST /api/pedidos. Antes pasan los middleware: leer el JSON, comprobar la sesión, validar los datos. - La función guarda el pedido en MongoDB (con Mongoose, desde el módulo 5) y la base le devuelve el documento con su identificador.
- Express responde 201 Created con el pedido guardado en JSON.
- React recibe la respuesta, actualiza su estado y la pantalla muestra «Pedido recibido» sin recargar la página.
En el código, los pasos 1, 5 y 6 se ven así (la práctica del final los ejecuta completos):
// React (paso 1): enviar el pedido
const respuesta = await fetch("/api/pedidos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ platillo: "Tacos al pastor", cantidad: 3 }),
});
// paso 6: leer la respuesta
console.log(respuesta.status, await respuesta.json());
// 201 { id: 1, platillo: 'Tacos al pastor', cantidad: 3, estado: 'recibido' }
JSON: el idioma común
JSON (JavaScript Object Notation, estándar ECMA-404 y RFC 8259) es un formato de texto para representar datos. Se parece a un objeto de JavaScript, pero no es lo mismo: es lo que viaja por la red, y del otro lado hay que convertirlo de vuelta en objeto.
| Operación | Función | Ejemplo |
|---|---|---|
| Objeto → texto JSON (para enviar) | JSON.stringify(objeto) | { total: 45 } → '{"total":45}' |
| Texto JSON → objeto (al recibir) | JSON.parse(texto) | '{"total":45}' → { total: 45 } |
JSON solo admite seis tipos de valor: cadena, número, booleano, null, arreglo y objeto. Lo demás se transforma o se pierde al convertir:
| Valor en JavaScript | Qué hace JSON.stringify |
|---|---|
undefined, funciones, símbolos (como propiedad de un objeto) | Omite la propiedad completa |
undefined, funciones, símbolos (dentro de un arreglo) | Los cambia por null |
Date | La convierte en texto ISO ("2026-10-10T18:00:00.000Z"); al recibirla ya no es Date |
NaN e Infinity | Los cambia por null |
BigInt | Lanza un TypeError |
Esto importa en MERN porque los datos cruzan dos veces la frontera de texto (navegador → API → base de datos). MongoDB guarda internamente BSON, una versión binaria de JSON con más tipos (fechas reales, ObjectId, números decimales), y Mongoose se encarga de convertir en el módulo 5.
Por qué un solo lenguaje
| Ventaja | En la práctica |
|---|---|
| Una sola sintaxis de punta a punta | Aprendes JavaScript una vez y lo usas en pantalla, servidor y consultas |
| Un solo gestor de paquetes | npm instala lo del frontend y lo del backend |
| Código compartido | Las mismas reglas de validación pueden correr en el formulario y en la API |
| Datos sin traducción | Un documento de MongoDB, un objeto de Express y el estado de React tienen la misma forma |
Los límites también existen: JavaScript es de tipado dinámico (los errores de tipo aparecen al ejecutar; por eso el siguiente paso natural es TypeScript), y Node.js corre el código de JavaScript en un solo hilo, así que los cálculos pesados de CPU bloquean el servidor si no se mandan a otro proceso.
MERN frente a otras pilas
| Pila | Componentes | Cuándo se elige |
|---|---|---|
| MERN | MongoDB, Express, React, Node.js | Apps con datos de forma variable, un solo lenguaje, ecosistema enorme de React |
| MEAN | Igual, con Angular en lugar de React | Equipos que prefieren un framework completo y opinado en el frontend |
| PERN | PostgreSQL en lugar de MongoDB | Datos muy relacionados y transacciones estrictas (pagos, inventarios contables) |
| Next.js full stack | React + rutas de servidor en un solo proyecto | Sitios que necesitan SEO y renderizado en servidor; suele combinarse con MongoDB o PostgreSQL |
| Django / Laravel | Python o PHP, con su propio sistema de plantillas y ORM | Equipos que ya dominan esos lenguajes; paneles administrativos rápidos |
La decisión entre MongoDB y una base SQL la explicamos a fondo en Bases de datos: SQL y NoSQL.
Cuándo usar MERN y cuándo no
Conviene cuando:
- La app tiene usuarios, formularios y datos que cambian seguido: pedidos, reservas, catálogos, paneles internos.
- La estructura de los datos todavía se está descubriendo (MongoDB no exige definir tablas antes).
- Quieres que una sola persona o un equipo pequeño cubra frontend y backend.
- Necesitas una interfaz muy interactiva: carritos, filtros en vivo, tableros.
Mejor otra opción cuando:
- Los datos son muy relacionales y la consistencia es crítica (contabilidad, banca): PostgreSQL encaja mejor.
- El sitio es casi todo contenido y vive de Google: un generador estático o Next.js con renderizado en servidor.
- El trabajo pesado es de cálculo o ciencia de datos: Python suele tener mejores herramientas.
El proyecto del curso: Taquería en línea
A lo largo de los 8 módulos construimos un sistema de pedidos real. Requisitos:
| Rol | Puede |
|---|---|
| Cliente | Ver el menú con fotos, buscar y filtrar, armar un carrito, hacer un pedido y ver su estado |
| Dueño | Iniciar sesión, dar de alta y editar platillos con foto, ver los pedidos y cambiar su estado |
API prevista (se construye en los módulos 4 a 6):
| Método | Ruta | Qué hace | Acceso |
|---|---|---|---|
GET | /api/platillos | Lista el menú (con búsqueda, filtro y paginación) | Público |
POST | /api/platillos | Da de alta un platillo con foto | Dueño |
POST | /api/pedidos | Crea un pedido | Cliente |
GET | /api/pedidos | Lista los pedidos | Dueño |
PATCH | /api/pedidos/:id | Cambia el estado (recibido, preparando, listo) | Dueño |
POST | /api/sesion | Inicia sesión y entrega un token JWT | Público |
El código completo vive en el repositorio público ruta-mern-taqueria, con una carpeta por módulo.
Ruta de módulos
| Módulo | Tema | Qué se construye |
|---|---|---|
| 0 | Qué es MERN | Entorno listo y el recorrido de un pedido en Node puro |
| 1 | HTML, CSS y JavaScript | La página del menú, adaptable al celular |
| 2 | Git y GitHub | El proyecto con historial de versiones y en GitHub |
| 3 | React | Menú interactivo y carrito |
| 4 | Node.js y Express | API de platillos y pedidos |
| 5 | MongoDB | Los pedidos se guardan en MongoDB Atlas |
| 6 | Todo junto + login | Panel del dueño con JWT, roles y fotos |
| 7 | Publicar | App en internet: Vercel, Render y Atlas |
Práctica: prepara tu entorno y recorre un pedido
- Instala Node.js LTS desde nodejs.org. Incluye npm.
- Instala un editor: Visual Studio Code.
- Instala Git (lo usamos a fondo en el módulo 2).
- Comprueba las versiones en una terminal:
node -v # v22 o superior (este módulo se probó con v24.14.1)
npm -v
git --version
- Crea el archivo
recorrido.mjscon este contenido. Es un servidor y un cliente en el mismo archivo, sin instalar nada: el servidor hace el papel de Express y MongoDB (por ahora guarda en memoria) y elfetchdel final hace el papel de React.
// recorrido.mjs — el viaje completo de un pedido, solo con Node (sin instalar nada).
// El servidor hace de Express + MongoDB; el fetch del final hace de React.
import http from "node:http";
const pedidos = []; // por ahora, la "despensa" vive en memoria
const servidor = http.createServer((req, res) => {
if (req.method === "POST" && req.url === "/api/pedidos") {
let cuerpo = "";
req.on("data", (trozo) => (cuerpo += trozo));
req.on("end", () => {
const pedido = { id: pedidos.length + 1, ...JSON.parse(cuerpo), estado: "recibido" };
pedidos.push(pedido);
res.writeHead(201, { "Content-Type": "application/json" });
res.end(JSON.stringify(pedido));
});
return;
}
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Ruta no encontrada" }));
});
servidor.listen(3000, async () => {
console.log("Cocina abierta en http://localhost:3000");
const respuesta = await fetch("http://localhost:3000/api/pedidos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ platillo: "Tacos al pastor", cantidad: 3 }),
});
console.log(respuesta.status, await respuesta.json());
servidor.close();
});
- Ejecútalo:
node recorrido.mjs
Salida esperada:
Cocina abierta en http://localhost:3000
201 { id: 1, platillo: 'Tacos al pastor', cantidad: 3, estado: 'recibido' }
Ya recorriste las tres capas: un cliente que envía JSON, un servidor que lo recibe, lo guarda y responde 201. En los siguientes módulos cada pieza se cambia por la herramienta profesional: React en lugar del fetch suelto, Express en lugar de http y MongoDB en lugar del arreglo en memoria.
Quiz del módulo
¿Qué imprime este código?
const pedido = { taco: "pastor", salsa: undefined, total: 45 };
console.log(JSON.stringify(pedido));
{"taco":"pastor","salsa":undefined,"total":45}{"taco":"pastor","salsa":null,"total":45}{"taco":"pastor","total":45}- Un error
Respuesta: 3. JSON no tiene undefined. Al convertir un objeto, JSON.stringify omite las propiedades cuyo valor es undefined (también las funciones y los símbolos). Por eso salsa desaparece y el servidor nunca se entera de que existía. Si necesitas mandar «sin salsa», usa null o false: esos valores sí existen en JSON. Pruébalo tú: node -e 'console.log(JSON.stringify({taco:"pastor",salsa:undefined,total:45}))'.
Referencias
Un restaurante. React es el salón con el menú que ves; Express es el mesero que lleva tu pedido; Node.js es la cocina donde todo funciona; MongoDB es la despensa donde se guarda todo. Y todos hablan el mismo idioma: JavaScript.
Glosario: del inglés al español
| Stack | pila: el conjunto de tecnologías con que se construye una app |
|---|---|
| Full stack | de punta a punta: pantalla, servidor y datos |
| Frontend | la parte que ves y tocas (corre en el navegador) |
| Backend | la parte que no ves (corre en el servidor) |
| JSON | notación de objetos de JavaScript: el formato de texto con que se mandan los datos |
| Runtime | entorno de ejecución: el programa que corre tu código |