Programación Web · Capítulo 13

Node.js y el Backend: Cómo Funciona el Servidor

Descubre cómo construir servidores web con JavaScript usando Node.js y Express, el framework más popular del ecosistema.


1. Frontend vs Backend: La División del Trabajo

En el desarrollo web moderno, hay una clara separación de responsabilidades entre lo que ocurre en el navegador del usuario y lo que ocurre en los servidores remotos.

AspectoFrontendBackend
Dónde correNavegador del usuarioServidor remoto
TecnologíasHTML, CSS, JavaScript, ReactNode.js, Python, Java, PHP
ResponsabilidadInterfaz visual, interacciónLógica de negocio, base de datos
El usuario lo veNo directamente
SeguridadNunca guardar secretos aquíClaves, contraseñas, lógica sensible

2. Node.js: JavaScript en el Servidor

Node.js es un entorno de ejecución de JavaScript construido sobre el motor V8 de Chrome. Permite ejecutar JavaScript fuera del navegador — en servidores, herramientas de línea de comandos y más. Su característica clave es la I/O no bloqueante: puede manejar miles de conexiones simultáneas sin crear un hilo por cada una.

npm y package.json

# Inicializar un proyecto nuevo npm init -y # Instalar una dependencia npm install express # Instalar dependencia de desarrollo (solo para desarrollo) npm install --save-dev nodemon # Ejecutar scripts definidos en package.json npm run dev npm start
// package.json generado { "name": "mi-servidor", "version": "1.0.0", "description": "API REST con Express", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "dependencies": { "express": "^4.18.2", "cors": "^2.8.5", "dotenv": "^16.0.3" }, "devDependencies": { "nodemon": "^3.0.0" } }

3. Servidor HTTP Básico con Node.js

// servidor-basico.js const http = require("http"); const servidor = http.createServer((req, res) => { // req = solicitud del cliente // res = respuesta que enviamos res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ mensaje: "¡Hola desde Node.js!" })); }); servidor.listen(3000, () => { console.log("Servidor corriendo en http://localhost:3000"); });

4. Express.js: El Framework Esencial

Express simplifica enormemente el trabajo con Node.js. Añade routing, middleware, y utilidades para manejar requests y responses de forma cómoda.

// index.js — servidor Express completo const express = require("express"); const cors = require("cors"); require("dotenv").config(); const app = express(); const PORT = process.env.PORT || 3000; // ── Middleware ──────────────────────────────────────────── app.use(cors()); // Permitir solicitudes desde otros dominios app.use(express.json()); // Parsear body en formato JSON app.use(express.urlencoded({ extended: true })); // Parsear formularios HTML // Middleware de logging personalizado app.use((req, res, next) => { console.log(`${new Date().toISOString()} ${req.method} ${req.url}`); next(); // IMPORTANTE: pasar al siguiente middleware/ruta }); // ── Datos en memoria (en producción usarías una BD) ─────── let productos = [ { id: 1, nombre: "Laptop", precio: 999, stock: 15 }, { id: 2, nombre: "Mouse", precio: 45, stock: 80 }, { id: 3, nombre: "Teclado", precio: 75, stock: 40 } ]; // ── Rutas ───────────────────────────────────────────────── // GET: obtener todos los productos app.get("/api/productos", (req, res) => { const { categoria, minPrecio } = req.query; // Query params: ?categoria=tech let resultado = [...productos]; if (minPrecio) { resultado = resultado.filter(p => p.precio >= Number(minPrecio)); } res.json({ total: resultado.length, datos: resultado }); }); // GET: obtener un producto por ID app.get("/api/productos/:id", (req, res) => { const id = parseInt(req.params.id); // Parámetros de ruta: /api/productos/1 const producto = productos.find(p => p.id === id); if (!producto) { return res.status(404).json({ error: "Producto no encontrado" }); } res.json(producto); }); // POST: crear un producto app.post("/api/productos", (req, res) => { const { nombre, precio, stock } = req.body; // Validación básica if (!nombre || !precio) { return res.status(400).json({ error: "nombre y precio son obligatorios" }); } const nuevoProducto = { id: productos.length + 1, nombre, precio: Number(precio), stock: stock || 0 }; productos.push(nuevoProducto); res.status(201).json(nuevoProducto); }); // PUT: actualizar un producto completo app.put("/api/productos/:id", (req, res) => { const id = parseInt(req.params.id); const index = productos.findIndex(p => p.id === id); if (index === -1) { return res.status(404).json({ error: "Producto no encontrado" }); } productos[index] = { id, ...req.body }; res.json(productos[index]); }); // DELETE: eliminar un producto app.delete("/api/productos/:id", (req, res) => { const id = parseInt(req.params.id); const index = productos.findIndex(p => p.id === id); if (index === -1) { return res.status(404).json({ error: "Producto no encontrado" }); } const eliminado = productos.splice(index, 1)[0]; res.json({ mensaje: "Producto eliminado", producto: eliminado }); }); // Manejo de rutas no encontradas app.use((req, res) => { res.status(404).json({ error: "Ruta no encontrada" }); }); // ── Iniciar servidor ────────────────────────────────────── app.listen(PORT, () => { console.log(`Servidor corriendo en http://localhost:${PORT}`); });

5. Variables de Entorno con .env

# .env — NUNCA subir este archivo a Git PORT=3000 DATABASE_URL=mysql://usuario:contraseña@localhost:3306/mibasedatos JWT_SECRET=mi_clave_secreta_muy_larga_y_aleatoria NODE_ENV=development
// Usando variables de entorno en el código require("dotenv").config(); const puerto = process.env.PORT; const dbUrl = process.env.DATABASE_URL; const esProduccion = process.env.NODE_ENV === "production"; if (!process.env.JWT_SECRET) { throw new Error("JWT_SECRET no está configurado"); }

6. Estructura de Proyecto Express Escalable

mi-api/ ├── src/ │ ├── routes/ │ │ ├── productos.js # Rutas de productos │ │ └── usuarios.js # Rutas de usuarios │ ├── controllers/ │ │ ├── productosController.js │ │ └── usuariosController.js │ ├── middleware/ │ │ ├── auth.js # Verificar JWT │ │ └── validacion.js # Validar inputs │ ├── models/ │ │ └── Producto.js # Interacción con BD │ └── config/ │ └── database.js # Conexión a BD ├── .env ├── .gitignore ├── index.js # Punto de entrada └── package.json
// src/routes/productos.js — separar rutas en archivos const express = require("express"); const router = express.Router(); const { listar, obtener, crear, actualizar, eliminar } = require("../controllers/productosController"); const { verificarToken } = require("../middleware/auth"); router.get("/", listar); router.get("/:id", obtener); router.post("/", verificarToken, crear); // Requiere autenticación router.put("/:id", verificarToken, actualizar); router.delete("/:id", verificarToken, eliminar); module.exports = router; // En index.js: // app.use("/api/productos", require("./src/routes/productos"));

Resumen del Capítulo