5-5-MVC-API

 MVC sin Router y sin Views separadas (el HTML va directamente en el controlador web, sin EJS). La estructura es simple y mantiene la separación de responsabilidades.

📁 Estructura del proyecto

text
mvc-simple/
├── app.js
├── models/
│   └── productoModel.js
└── controllers/
    ├── webController.js
    └── apiController.js

(No hay carpetas views ni routes)


1. Modelo (models/productoModel.js)

javascript
// Base de datos en memoria
let productos = [
  { id: 1, nombre: 'Laptop', precio: 1000 },
  { id: 2, nombre: 'Mouse', precio: 25 },
  { id: 3, nombre: 'Teclado', precio: 50 }
];

// Obtener todos
const getAll = () => productos;

// Obtener por ID
const getById = (id) => productos.find(p => p.id === id);

// Crear nuevo producto (devuelve el producto creado)
const create = (nombre, precio) => {
  const nuevoId = productos.length > 0 ? Math.max(...productos.map(p => p.id)) + 1 : 1;
  const nuevoProducto = { id: nuevoId, nombre, precio: parseFloat(precio) };
  productos.push(nuevoProducto);
  return nuevoProducto;
};

module.exports = { getAll, getById, create };

2. Controlador Web (controllers/webController.js)

Maneja las peticiones de la interfaz web y genera el HTML directamente (sin archivos de vista).

javascript
const model = require('../models/productoModel');

// GET / - Mostrar todos los productos (HTML)
const listar = (req, res) => {
  const productos = model.getAll();
  
  let listaHTML = '';
  for (let p of productos) {
    listaHTML += `
      <li>
        <strong>${p.nombre}</strong> - $${p.precio}
        (<a href="/productos/${p.id}">Ver detalles</a>)
      </li>
    `;
  }

  res.send(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>Productos</title>
      <style>body{font-family:Arial;margin:30px;} li{margin:10px 0;}</style>
    </head>
    <body>
      <h1>📦 Lista de productos</h1>
      <ul>${listaHTML}</ul>
      <hr>
      <h2>➕ Agregar producto</h2>
      <form action="/productos" method="POST">
        <input type="text" name="nombre" placeholder="Nombre" required>
        <input type="number" step="1" name="precio" placeholder="Precio" required>
        <button type="submit">Crear</button>
      </form>
      <hr>
      <p><strong>🔗 Endpoints API (JSON):</strong> <a href="/api/productos">/api/productos</a></p>
    </body>
    </html>
  `);
};

// GET /productos/:id - Mostrar detalle (HTML)
const mostrarDetalle = (req, res) => {
  const id = parseInt(req.params.id);
  const producto = model.getById(id);
  if (!producto) {
    return res.status(404).send(`
      <!DOCTYPE html>
      <html>
      <head><title>Error 404</title></head>
      <body>
        <h1>404 - Producto no encontrado</h1>
        <a href="/">Volver</a>
      </body>
      </html>
    `);
  }
  res.send(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>${producto.nombre}</title>
      <style>body{font-family:Arial;margin:30px;}</style>
    </head>
    <body>
      <h1>📄 Detalle del producto</h1>
      <p><strong>ID:</strong> ${producto.id}</p>
      <p><strong>Nombre:</strong> ${producto.nombre}</p>
      <p><strong>Precio:</strong> $${producto.precio}</p>
      <a href="/">← Volver al listado</a>
    </body>
    </html>
  `);
};

// POST /productos - Crear producto desde formulario (redirige)
const crear = (req, res) => {
  const { nombre, precio } = req.body;
  if (!nombre || !precio) {
    return res.status(400).send(`
      <!DOCTYPE html>
      <html>
      <head><title>Error</title></head>
      <body>
        <h1>Error: faltan nombre o precio</h1>
        <a href="/">Volver</a>
      </body>
      </html>
    `);
  }
  model.create(nombre, precio);
  res.redirect('/');
};

module.exports = { listar, mostrarDetalle, crear };

3. Controlador API (controllers/apiController.js)

Maneja las peticiones REST y responde con JSON.

javascript
const model = require('../models/productoModel');

// GET /api/productos - Obtener todos (JSON)
const obtenerTodos = (req, res) => {
  const productos = model.getAll();
  res.json(productos);
};

// GET /api/productos/:id - Obtener uno (JSON)
const obtenerUno = (req, res) => {
  const id = parseInt(req.params.id);
  const producto = model.getById(id);
  if (!producto) {
    return res.status(404).json({ error: 'Producto no encontrado' });
  }
  res.json(producto);
};

// POST /api/productos - Crear producto (JSON)
const crear = (req, res) => {
  const { nombre, precio } = req.body;
  if (!nombre || typeof precio !== 'number') {
    return res.status(400).json({ error: 'Se requiere nombre y precio (número)' });
  }
  const nuevoProducto = model.create(nombre, precio);
  res.status(201).json(nuevoProducto);
};

module.exports = { obtenerTodos, obtenerUno, crear };

4. Aplicación principal (app.js)

Configura Express, middlewares y define las rutas directamente (sin Router).

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

// Middlewares
app.use(express.json());                      // Para JSON (API)
app.use(express.urlencoded({ extended: true })); // Para formularios web

// Importar controladores
const webController = require('./controllers/webController');
const apiController = require('./controllers/apiController');

// ========== RUTAS WEB (HTML) ==========
app.get('/', webController.listar);
app.get('/productos/:id', webController.mostrarDetalle);
app.post('/productos', webController.crear);

// ========== RUTAS API (JSON) con prefijo /api ==========
app.get('/api/productos', apiController.obtenerTodos);
app.get('/api/productos/:id', apiController.obtenerUno);
app.post('/api/productos', apiController.crear);

// Iniciar servidor
app.listen(PORT, () => {
  console.log(`✅ Servidor MVC (sin router, sin views) en http://localhost:${PORT}`);
  console.log('🌐 Interfaz web:');
  console.log('   - Lista: http://localhost:3000/');
  console.log('   - Detalle: http://localhost:3000/productos/1');
  console.log('🔌 API JSON:');
  console.log('   - GET  /api/productos');
  console.log('   - GET  /api/productos/:id');
  console.log('   - POST /api/productos');
});

▶️ Cómo ejecutar

  1. Crear las carpetas y archivos con los códigos anteriores.

  2. Instalar Express:

    bash
    npm init -y
    npm install express
  3. Ejecutar:

    bash
    node app.js
  4. Abrir http://localhost:3000 para la interfaz web.

  5. Probar la API con Postman o navegador:

    • GET http://localhost:3000/api/productos

    • GET http://localhost:3000/api/productos/2

    • POST http://localhost:3000/api/productos con JSON {"nombre":"Monitor","precio":200}


✅ Características de esta versión

  • MVC puro: Modelo, Controladores (web y API), y las "vistas" están dentro del controlador web (HTML embebido).

  • Sin Router: Las rutas se definen directamente en app.js.

  • Sin archivos de vista separados: No se usa EJS ni ningún motor de plantillas; el HTML se genera en el controlador.

  • Fácil de entender: Ideal para alumnos que ya conocen el ejemplo original y quieren ver la misma funcionalidad pero organizada en capas.

Comentarios

Entradas más populares de este blog

Cómo Iniciar un Proyecto Node.js

8-Template Engines

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