7-assets--express.static('public'

7-assets--express.static('public'


 ¿Qué son los Assets?

Assets (o "activos" en español) son todos los archivos estáticos que componen la interfaz visual y funcional de una aplicación web.

📦 Tipos de Assets

Tipo

Extensiones

Ejemplos

CSS

.css, .scss, .sass

estilos, layouts, animaciones

JavaScript

.js, .mjs

lógica frontend, interacciones

Imágenes

.jpg, .png, .gif, .svg, .webp

logos, fotos, iconos, ilustraciones

Fuentes

.woff, .woff2, .ttf, .otf

tipografías personalizadas

Archivos estáticos

.html, .xml, .json

templates, datos de configuración

Media

.mp4, .webm, .mp3

videos, audios

🎯 Características de los Assets

  1. No cambian dinámicamente - Son iguales para todos los usuarios

  2. Se sirven directamente - No requieren procesamiento en el servidor

  3. Se cachean en el navegador - Mejoran el rendimiento

  4. Se organizan en carpetas - CSS, JS, images, fonts, etc.

📁 Estructura Típica de Assets

text

mi-proyecto/

├── public/              ← Raíz de assets

│   ├── css/

│   │   ├── main.css

│   │   ├── reset.css

│   │   └── responsive.css

│   ├── js/

│   │   ├── main.js

│   │   ├── utils.js

│   │   └── vendor/          ← Librerías externas

│   ├── images/

│   │   ├── logo.png

│   │   ├── background.jpg

│   │   └── icons/

│   ├── fonts/

│   │   └── roboto.woff2

│   └── assets/              ← Carpeta genérica

│       └── ...

💻 En Express con express.static

javascript

const express = require('express');

const app = express();


// Servir todos los assets desde 'public'

app.use(express.static('public'));


// Ahora estos archivos son accesibles:

// http://localhost:3000/css/main.css

// http://localhost:3000/js/main.js

// http://localhost:3000/images/logo.png

🌐 En el HTML (Frontend)

html

<!DOCTYPE html>

<html>

<head>

    <!-- CSS Assets -->

    <link rel="stylesheet" href="/css/main.css">

    <link rel="stylesheet" href="/css/responsive.css">

    

    <!-- Font Asset -->

    <link href="/fonts/roboto.woff2" rel="preload">

</head>

<body>

    <!-- Image Assets -->

    <img src="/images/logo.png" alt="Logo">

    <img src="/images/icons/menu.svg" alt="Menú">

    

    <!-- JavaScript Assets -->

    <script src="/js/vendor/jquery.js"></script>

    <script src="/js/main.js"></script>

</body>

</html>

🔄 Assets vs Archivos Dinámicos

Característica

Assets

Archivos Dinámicos

Generación

Archivos fijos

Se crean al vuelo

Ejemplos

CSS, imágenes, JS frontend

API responses, vistas renderizadas

Extensión

.css, .png, .js

.ejs, .hbs, JSON de API

Caché

Larga duración

Corta o sin caché

Middleware

express.static

Rutas personalizadas

📊 Ejemplo Práctico

javascript

const express = require('express');

const app = express();


// ✅ Assets (estáticos)

app.use('/css', express.static('public/css'));

app.use('/js', express.static('public/js'));

app.use('/img', express.static('public/images'));


// ✅ Archivo dinámico (generado en servidor)

app.get('/user/:id', (req, res) => {

    // Esto NO es un asset - se genera para cada usuario

    res.json({ id: req.params.id, name: 'Usuario' });

});


app.listen(3000);

🚀 Buenas Prácticas con Assets

  1. Organización clara

  2. text

public/

├── assets/

│   ├── styles/

│   ├── scripts/

  1. │   └── media/

  2. Optimización

    • Minificar CSS/JS en producción

    • Comprimir imágenes (WebP, AVIF)

    • Usar versionado (cache busting)

  3. Headers de caché

  4. javascript

app.use(express.static('public', {

    maxAge: '30d',  // Assets cacheados por 30 días

    immutable: true  // No cambiarán

  1. }));

🎯 Resumen

  • Assets = Archivos estáticos (CSS, JS, imágenes, fuentes)

  • No cambian con cada request

  • Se sirven con express.static

  • Se organizan en carpetas como public/ o assets/

  • Mejoran rendimiento con caché

En términos simples: todo lo que ves y escuchas en una web que no es generado en el momento son assets 

Estructura del proyecto:

text
mi-app/
├── app.js
├── views/
│   └── index.ejs
└── package.json

app.js

javascript
const express = require('express');
const app = express();
const PORT = 3000;

// ============ CONFIGURAR MOTOR DE PLANTILLAS ============
app.set('view engine', 'ejs');
app.set('views', './views');

// ============ MIDDLEWARE ============
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/css', express.static('node_modules/bootstrap/dist/css'));
app.use('/js', express.static('node_modules/bootstrap/dist/js'));

// ============ DATOS DE EJEMPLO ============
let productos = [
    { id: 1, nombre: 'Laptop', precio: 1000 },
    { id: 2, nombre: 'Mouse', precio: 25 },
    { id: 3, nombre: 'Teclado', precio: 50 }
];

// ============ RUTAS CON VISTAS (HTML) ============

// Ruta principal - Muestra todos los productos y formulario
app.get('/', (req, res) => {
    res.render('index', {
        titulo: 'Mi Tienda',
        productos: productos
    });
});

// ============ RUTAS API (JSON) ============

// GET - Obtener todos los productos
app.get('/api/productos', (req, res) => {
    res.json({
        success: true,
        cantidad: productos.length,
        productos: productos
    });
});

// GET - Obtener un producto específico
app.get('/api/productos/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const producto = productos.find(p => p.id === id);
    
    if (!producto) {
        return res.status(404).json({ 
            success: false,
            error: 'Producto no encontrado' 
        });
    }
    
    res.json(producto);
});

// POST - Crear nuevo producto (API)
app.post('/api/productos', (req, res) => {
    const { nombre, precio } = req.body;
    
    if (!nombre || !precio) {
        return res.status(400).json({ 
            success: false,
            error: 'Faltan datos: nombre y precio son requeridos' 
        });
    }
    
    const nuevoProducto = {
        id: productos.length + 1,
        nombre: nombre,
        precio: parseFloat(precio)
    };
    
    productos.push(nuevoProducto);
    
    res.json({
        success: true,
        producto: nuevoProducto,
        mensaje: 'Producto creado exitosamente'
    });
});

// ============ RUTAS CON FORMULARIOS ============

// POST - Crear producto desde el formulario HTML
app.post('/crear-producto', (req, res) => {
    const { nombre, precio } = req.body;
    
    if (!nombre || !precio) {
        return res.status(400).send('Error: Faltan datos. <a href="/">Volver</a>');
    }
    
    const nuevoProducto = {
        id: productos.length + 1,
        nombre: nombre,
        precio: parseFloat(precio)
    };
    
    productos.push(nuevoProducto);
    res.redirect('/');
});

// ============ INICIAR SERVIDOR ============
app.listen(PORT, () => {
    console.log(`✅ Servidor en http://localhost:${PORT}`);
    console.log(`📱 Página web: http://localhost:${PORT}`);
    console.log(`🔌 API: http://localhost:${PORT}/api/productos`);
});

views/index.ejs

ejs
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= titulo %></title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center mb-4">🛒 <%= titulo %></h1>
        
        <div class="row">
            <!-- Formulario para crear producto -->
            <div class="col-md-5">
                <div class="card">
                    <div class="card-header bg-primary text-white">
                        <h4 class="mb-0">📝 Agregar Producto</h4>
                    </div>
                    <div class="card-body">
                        <form action="/crear-producto" method="POST">
                            <div class="mb-3">
                                <label for="nombre" class="form-label">Nombre del producto:</label>
                                <input type="text" class="form-control" id="nombre" name="nombre" required>
                            </div>
                            <div class="mb-3">
                                <label for="precio" class="form-label">Precio (USD):</label>
                                <input type="number" class="form-control" id="precio" name="precio" step="0.01" required>
                            </div>
                            <button type="submit" class="btn btn-primary w-100">Agregar Producto</button>
                        </form>
                    </div>
                </div>
            </div>
            
            <!-- Lista de productos -->
            <div class="col-md-7">
                <div class="card">
                    <div class="card-header bg-success text-white">
                        <h4 class="mb-0">📦 Productos</h4>
                    </div>
                    <div class="card-body">
                        <% if (productos.length > 0) { %>
                            <table class="table table-striped table-hover">
                                <thead>
                                    <tr>
                                        <th>ID</th>
                                        <th>Nombre</th>
                                        <th>Precio</th>
                                        <th>Estado</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <% productos.forEach(producto => { %>
                                        <tr>
                                            <td><span class="badge bg-secondary">#<%= producto.id %></span></td>
                                            <td><strong><%= producto.nombre %></strong></td>
                                            <td class="text-success fw-bold">$<%= producto.precio.toFixed(2) %></td>
                                            <td><span class="badge bg-success">Disponible</span></td>
                                        </tr>
                                    <% }) %>
                                </tbody>
                            </table>
                            
                            <div class="alert alert-info mt-3">
                                <strong>📊 Total:</strong> <%= productos.length %> productos
                            </div>
                        <% } else { %>
                            <div class="alert alert-warning text-center">
                                No hay productos disponibles. ¡Agrega el primero!
                            </div>
                        <% } %>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- Información de la API -->
        <div class="row mt-4">
            <div class="col-12">
                <div class="card">
                    <div class="card-header bg-dark text-white">
                        <h4 class="mb-0">🔌 Endpoints API</h4>
                    </div>
                    <div class="card-body">
                        <ul class="list-group">
                            <li class="list-group-item">
                                <strong>GET /api/productos</strong> - Ver todos los productos (JSON)
                                <a href="/api/productos" target="_blank" class="btn btn-sm btn-info float-end">Probar</a>
                            </li>
                            <li class="list-group-item">
                                <strong>GET /api/productos/:id</strong> - Ver un producto específico (JSON)
                                <a href="/api/productos/1" target="_blank" class="btn btn-sm btn-info float-end">Probar ID 1</a>
                            </li>
                            <li class="list-group-item">
                                <strong>POST /api/productos</strong> - Crear producto via API (JSON)
                                <button class="btn btn-sm btn-secondary float-end" onclick="mostrarEjemplo()">Ver ejemplo</button>
                            </li>
                        </ul>
                        
                        <div id="ejemploApi" class="alert alert-secondary mt-3" style="display:none;">
                            <strong>💡 Ejemplo con curl:</strong><br>
                            <code>curl -X POST http://localhost:3000/api/productos \<br>
                            -H "Content-Type: application/json" \<br>
                            -d '{"nombre":"Tablet","precio":300}'</code>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <script src="/js/bootstrap.bundle.min.js"></script>
    <script>
        function mostrarEjemplo() {
            const ejemplo = document.getElementById('ejemploApi');
            if (ejemplo.style.display === 'none') {
                ejemplo.style.display = 'block';
            } else {
                ejemplo.style.display = 'none';
            }
        }
    </script>
</body>
</html>

package.json

json
{
  "name": "mi-app",
  "version": "1.0.0",
  "description": "App sencilla con Express y Bootstrap",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "ejs": "^3.1.9",
    "bootstrap": "^5.3.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

Instalación y ejecución:

bash
# 1. Crear carpeta del proyecto
mkdir mi-app
cd mi-app

# 2. Crear archivo package.json
npm init -y

# 3. Instalar dependencias
npm install express ejs bootstrap

# 4. Instalar nodemon (opcional, para desarrollo)
npm install -D nodemon

# 5. Crear carpeta views
mkdir views

# 6. Crear los archivos:
#    - app.js (copiar el código)
#    - views/index.ejs (copiar el código)

# 7. Ejecutar la aplicación
npm start
# o con nodemon para desarrollo automático
npm run dev

Características de esta versión:

Bootstrap 5 - Estilos modernos sin CSS personalizado
Diseño responsive - Se adapta a móviles y tablets
Formulario para crear productos
Tabla de productos con Bootstrap
API REST completa (GET y POST)
Interfaz limpia y profesional

Comentarios

Entradas más populares de este blog

Cómo Iniciar un Proyecto Node.js

5-Express--Curso de Node.js#05 Introducción a Express.js

8-Template Engines