Servicio de archivos estáticos y gestión de errores
Al finalizar este tema
Podrás servir archivos estáticos como HTML, CSS e imágenes con Express, y gestionar de forma estructurada los errores 404 (página no encontrada) y 500 (error del servidor).
¿Qué son los archivos estáticos?
Existen dos tipos de archivos que gestiona un servidor web:
- Archivos dinámicos: respuestas generadas por el servidor al ejecutar código en cada solicitud (API, resultados de consultas a bases de datos)
- Archivos estáticos: archivos que el servidor simplemente transmite sin modificaciones (HTML, CSS, JavaScript, imágenes, fuentes)
La lista de entradas de un blog es dinámica, pero la imagen del logotipo y las hojas de estilo del blog son estáticas. Dado que los archivos estáticos no necesitan crearse desde cero en cada solicitud, se gestionan de forma eficiente mediante un middleware especializado.
express.static: middleware para archivos estáticos
Express incluye el middleware express.static().
project/
├── app.js
├── public/
│ ├── index.html
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── main.js
│ └── images/
│ └── logo.pngconst express = require('express');
const path = require('path');
const app = express();
// Configurar la carpeta public como raíz de archivos estáticos
app.use(express.static(path.join(__dirname, 'public')));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});Ahora, al acceder a http://localhost:3000/css/style.css desde el navegador, se mostrará el archivo public/css/style.css. Ya no es necesario agregar /public a la URL, ya que express.static asigna la carpeta public como directorio raíz.
Agregar un prefijo a la URL
// Acceder a /assets/css/style.css
app.use('/assets', express.static(path.join(__dirname, 'public')));El primer argumento es el prefijo de la URL. De este modo, la ruta real del archivo será public/css/style.css, pero la URL será /assets/css/style.css. Esto es útil para separar las rutas con fines de CDN o control de versiones.
Especificación de varias carpetas
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'uploads')));Puede registrar ambas carpetas como fuentes de archivos estáticos. Si no se encuentran los archivos en la primera carpeta, se buscará en la segunda. El orden establece la prioridad.
path.join y __dirname: creación de rutas de forma segura
// Mal: ruta relativa — varía según la ubicación de ejecución
app.use(express.static('public'));
// Bien: ruta absoluta — funciona igual sin importar dónde se ejecute
app.use(express.static(path.join(__dirname, 'public')));__dirname es la ruta absoluta del directorio que contiene el archivo actual. path.join() gestiona automáticamente los separadores de ruta apropiados para el sistema operativo (/ o \). En Windows, escribir / directamente puede causar problemas; por lo tanto, siempre se debe usar path.join.
Manejo de errores 404: página no encontrada
En Express, si una solicitud no coincide con ninguna ruta, simplemente se ignora. El cliente esperará indefinidamente una respuesta. Para evitar esto, se debe colocar un controlador de errores 404 después de todas las rutas.
// Definir ruta
app.get('/', (req, res) => {
res.send('Home');
});
app.get('/about', (req, res) => {
res.send('About');
});
// Manejador 404 — después de todas las rutas, antes del manejador de errores
app.use((req, res) => {
res.status(404).json({
error: 'Not Found',
message: `${req.method} ${req.url} does not exist`,
status: 404
});
});La ubicación es importante. Dado que app.use() se ejecutan en secuencia, si se coloca el controlador de errores 404 al principio, todas las solicitudes devolverán un error 404. Debe colocarse siempre después de todas las definiciones de rutas.
Página HTML de 404
Si no es un servidor de API, sino un sitio web, es más lógico mostrar una página HTML en lugar de JSON.
app.use((req, res) => {
res.status(404).sendFile(path.join(__dirname, 'public', '404.html'));
});Manejo de errores 500 — Error interno del servidor
El middleware de manejo de errores tiene cuatro parámetros. Express distingue entre middleware estándar y manejadores de errores mediante esta firma.
// Manejador de errores — después del manejador 404
app.use((err, req, res, next) => {
console.error(`[ERROR] ${err.stack}`);
res.status(err.status || 500).json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'production'
? 'Something went wrong'
: err.message,
status: err.status || 500
});
});Dos puntos a tener en cuenta:
-
En el entorno de producción, los mensajes de error se ocultan. Esto se debe a que
err.messagepuede contener información confidencial, como consultas SQL o rutas de archivos. Los mensajes detallados solo se muestran en el entorno de desarrollo. -
Cómo generar errores: Al llamar a
next(err)dentro de una ruta, se redirige al controlador de errores.
app.get('/users/:id', async (req, res, next) => {
try {
const user = await findUser(req.params.id);
if (!user) {
const err = new Error('User not found');
err.status = 404;
return next(err);
}
res.json(user);
} catch (err) {
next(err); // Errores inesperados como errores de DB
}
});Estructura general — orden correcto
const express = require('express');
const path = require('path');
const app = express();
// 1. Middleware básico
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// 2. Rutas
app.get('/api/users', (req, res) => { /* ... */ });
app.post('/api/users', (req, res) => { /* ... */ });
// 3. Manejador de 404 (después de las rutas)
app.use((req, res) => {
res.status(404).json({ error: 'Not Found' });
});
// 4. Manejador de errores (siempre al final)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
app.listen(3000);Este orden corresponde a la estructura estándar de un proyecto Express: middleware base → rutas → 404 → error. Si se incumple este orden, se producirán comportamientos inesperados.
Errores comunes en la práctica
| Error | Síntoma | Solución |
|---|---|---|
| El controlador 404 se define antes que las rutas | Todas las solicitudes devuelven 404 | Mover el controlador 404 al final |
| El controlador de errores tiene 3 parámetros | Los errores no se capturan | Se requieren (err, req, res, next) parámetros |
La ruta express.static es relativa | No se encuentran los archivos si se ejecuta desde otra carpeta | Usar path.join(__dirname, ...) |
No se llama a next(err) en el controlador de errores | El controlador de errores no se ejecuta | Llamar a try/catch + next(err) |
Se expone err.stack en producción | Vulnerabilidad de seguridad | Usar la rama condicional de NODE_ENV |
Resumen clave
El manejo de archivos estáticos y errores cubre tanto los casos "cuando el servidor funciona correctamente" como "cuando no funciona". express.static sirve los archivos de manera eficiente, mientras que los controladores 404/500 garantizan respuestas significativas para el usuario incluso en situaciones excepcionales. Recordar el orden del middleware de Express (análisis → archivos estáticos → rutas → 404 → error) evita confusiones sobre dónde colocar cada elemento.