10.2-Ejemplo MVC con Express y app.route

 

🏗️ Ejemplo MVC con Express y app.route()

Aquí tienes el CRUD anterior reorganizado en arquitectura MVC (Modelo-Vista-Controlador):

📁 ESTRUCTURA DEL PROYECTO

text

mi-app-mvc/

├── models/

│   └── Producto.js

├── controllers/

│   └── productoController.js

├── routes/

│   └── productoRoutes.js

├── views/               (opcional, si usas EJS)

├── app.js

└── server.js

📄 CÓDIGO COMPLETO

1️⃣ models/Producto.js (Modelo - Datos y lógica de negocio)

javascript

// Modelo de Producto - Maneja los datos y operaciones con la "base de datos"

class Producto {

    constructor() {

        // Base de datos simulada

        this.productos = [

            { id: 1, nombre: 'Laptop', precio: 1000 },

            { id: 2, nombre: 'Mouse', precio: 25 },

            { id: 3, nombre: 'Teclado', precio: 50 }

        ];

        this.contadorId = 4;

    }


    // Obtener todos los productos

    obtenerTodos() {

        return this.productos;

    }


    // Obtener un producto por ID

    obtenerPorId(id) {

        return this.productos.find(p => p.id === id);

    }


    // Crear nuevo producto

    crear(nombre, precio) {

        const nuevoProducto = {

            id: this.contadorId++,

            nombre,

            precio: parseFloat(precio)

        };

        this.productos.push(nuevoProducto);

        return nuevoProducto;

    }


    // Actualizar producto

    actualizar(id, nombre, precio) {

        const index = this.productos.findIndex(p => p.id === id);

        if (index === -1) return null;

        

        this.productos[index] = {

            ...this.productos[index],

            nombre,

            precio: parseFloat(precio)

        };

        return this.productos[index];

    }


    // Eliminar producto

    eliminar(id) {

        const index = this.productos.findIndex(p => p.id === id);

        if (index === -1) return null;

        

        const eliminado = this.productos.splice(index, 1);

        return eliminado[0];

    }

}


module.exports = new Producto();

2️⃣ controllers/productoController.js (Controlador - Lógica de respuesta)

javascript

const Producto = require('../models/Producto');


// Controlador: Procesa las peticiones y envía respuestas

class ProductoController {

    

    // GET /productos - Obtener todos

    obtenerTodos(req, res) {

        const productos = Producto.obtenerTodos();

        res.json({

            success: true,

            total: productos.length,

            productos: productos

        });

    }


    // POST /productos - Crear nuevo

    crear(req, res) {

        const { nombre, precio } = req.body;

        

        // Validaciones

        if (!nombre || !precio) {

            return res.status(400).json({ 

                error: 'Faltan datos: nombre y precio son requeridos' 

            });

        }

        

        if (isNaN(precio)) {

            return res.status(400).json({ 

                error: 'El precio debe ser un número' 

            });

        }

        

        const nuevoProducto = Producto.crear(nombre, precio);

        res.status(201).json({

            mensaje: '✅ Producto creado exitosamente',

            producto: nuevoProducto

        });

    }


    // GET /productos/:id - Obtener uno

    obtenerUno(req, res) {

        const id = parseInt(req.params.id);

        const producto = Producto.obtenerPorId(id);

        

        if (!producto) {

            return res.status(404).json({ 

                error: `Producto con ID ${id} no encontrado` 

            });

        }

        

        res.json(producto);

    }


    // PUT /productos/:id - Actualizar

    actualizar(req, res) {

        const id = parseInt(req.params.id);

        const { nombre, precio } = req.body;

        

        if (!nombre || !precio) {

            return res.status(400).json({ 

                error: 'Faltan datos: nombre y precio son requeridos' 

            });

        }

        

        const productoActualizado = Producto.actualizar(id, nombre, precio);

        

        if (!productoActualizado) {

            return res.status(404).json({ 

                error: `Producto con ID ${id} no encontrado` 

            });

        }

        

        res.json({

            mensaje: '✅ Producto actualizado exitosamente',

            producto: productoActualizado

        });

    }


    // DELETE /productos/:id - Eliminar

    eliminar(req, res) {

        const id = parseInt(req.params.id);

        const productoEliminado = Producto.eliminar(id);

        

        if (!productoEliminado) {

            return res.status(404).json({ 

                error: `Producto con ID ${id} no encontrado` 

            });

        }

        

        res.json({

            mensaje: '✅ Producto eliminado exitosamente',

            producto: productoEliminado

        });

    }

}


module.exports = new ProductoController();

3️⃣ routes/productoRoutes.js (Rutas - Definición de endpoints con app.route)

javascript

const express = require('express');

const productoController = require('../controllers/productoController');


const router = express.Router();


// ============ USANDO app.route() ============

// Rutas para colección de productos

router.route('/productos')

    .get(productoController.obtenerTodos)    // GET /productos

    .post(productoController.crerar);         // POST /productos


// Rutas para producto específico

router.route('/productos/:id')

    .get(productoController.obtenerUno)      // GET /productos/:id

    .put(productoController.actualizar)      // PUT /productos/:id

    .delete(productoController.eliminar);    // DELETE /productos/:id


module.exports = router;

4️⃣ app.js (Configuración central)

javascript

const express = require('express');

const app = express();

const productoRoutes = require('./routes/productoRoutes');


// Middlewares globales

app.use(express.json());

app.use(express.urlencoded({ extended: true }));


// Archivos estáticos (opcional)

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


// ============ REGISTRAR RUTAS ============

app.use('/', productoRoutes);  // Todas las rutas estarán disponibles


// Ruta de bienvenida (opcional)

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

    res.send(`

        <h1>📦 API MVC con app.route()</h1>

        <h2>Endpoints disponibles:</h2>

        <ul>

            <li><strong>GET</strong> /productos - Ver todos</li>

            <li><strong>GET</strong> /productos/1 - Ver uno</li>

            <li><strong>POST</strong> /productos - Crear</li>

            <li><strong>PUT</strong> /productos/1 - Actualizar</li>

            <li><strong>DELETE</strong> /productos/1 - Eliminar</li>

        </ul>

        <h3>Ejemplo POST/PUT (JSON):</h3>

        <code>{"nombre": "Monitor", "precio": 300}</code>

    `);

});


// Manejo de errores 404

app.use((req, res) => {

    res.status(404).json({ error: 'Ruta no encontrada' });

});


module.exports = app;

5️⃣ server.js (Punto de entrada)

javascript

const app = require('./app');

const PORT = 3000;


app.listen(PORT, () => {

    console.log(`\n=================================`);

    console.log(`🚀 Servidor MVC corriendo en http://localhost:${PORT}`);

    console.log(`📦 API de Productos con app.route()`);

    console.log(`\n📌 Probar endpoints:`);

    console.log(`   GET    /productos`);

    console.log(`   GET    /productos/1`);

    console.log(`   POST   /productos`);

    console.log(`   PUT    /productos/1`);

    console.log(`   DELETE /productos/1`);

    console.log(`=================================\n`);

});

🚀 CÓMO EJECUTAR

bash

# 1. Instalar Express

npm install express


# 2. Ejecutar el servidor

node server.js

📊 FLUJO DE LA ARQUITECTURA MVC

text

Petición HTTP

    ↓

[Routes] app.route('/productos')

    ↓

[Controller] productoController.obtenerTodos()

    ↓

[Model] Producto.obtenerTodos()

    ↓

[Model] Base de datos simulada

    ↓

[Controller] res.json() respuesta

    ↓

Cliente (JSON)

💡 VENTAJAS DE ESTA ORGANIZACIÓN MVC

Capa

Responsabilidad

Beneficio

Modelo

Datos y lógica de negocio

Cambias la BD sin afectar controladores

Vista

Interfaz de usuario

(opcional con EJS)

Controlador

Lógica de respuesta

Validaciones y manejo de errores

Rutas

Definición de endpoints

Usando app.route() más limpio

🧪 PROBAR CON CURL

bash

# GET todos

curl http://localhost:3000/productos


# GET uno

curl http://localhost:3000/productos/1


# POST crear

curl -X POST http://localhost:3000/productos \

  -H "Content-Type: application/json" \

  -d '{"nombre":"Tablet","precio":200}'


# PUT actualizar

curl -X PUT http://localhost:3000/productos/1 \

  -H "Content-Type: application/json" \

  -d '{"nombre":"Ultrabook","precio":1200}'


# DELETE eliminar

curl -X DELETE http://localhost:3000/productos/2

🔄 SI QUIERES AGREGAR VISTAS EJS

javascript

// En app.js (agregar después de middlewares)

app.set('view engine', 'ejs');

app.set('views', './views');


// En productoController.js (agregar método)

mostrarVistaProductos(req, res) {

    const productos = Producto.obtenerTodos();

    res.render('productos', { 

        title: 'Lista de Productos',

        productos 

    });

}


// En productoRoutes.js (agregar ruta)

router.get('/productos-vista', productoController.mostrarVistaProductos);

¡Así queda un CRUD completo en MVC usando app.route()


Comentarios

Entradas más populares de este blog

Cómo Iniciar un Proyecto Node.js

6-Middleware?

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