Skip to main content

Overview

Sistema de Productos is a full-stack web application built with a modern separation of concerns architecture. The system consists of a Vue.js frontend and an Express.js backend API, connected through RESTful endpoints.

Technology Stack

Frontend

  • Vue.js 3 with Composition API
  • Vue Router for navigation
  • Axios for HTTP requests
  • Component-based architecture

Backend

  • Node.js with Express.js
  • PostgreSQL database
  • JWT authentication
  • MVC architecture pattern

Architecture Diagram

┌─────────────────────────────────────────────────────────┐
│                   Frontend (Vue.js)                     │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐             │
│  │  Views   │  │Components│  │   API    │             │
│  │          │  │          │  │  Client  │             │
│  └──────────┘  └──────────┘  └────┬─────┘             │
│                                    │                    │
└────────────────────────────────────┼────────────────────┘
                                     │ HTTP/HTTPS
                                     │ (localhost:5173)
┌────────────────────────────────────┼────────────────────┐
│                                    ▼                     │
│              Backend API (Express.js)                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐             │
│  │  Routes  │→ │Controller│→ │  Models  │             │
│  └──────────┘  └──────────┘  └────┬─────┘             │
│       ▲                            │                    │
│       │                            ▼                    │
│  ┌────┴──────┐            ┌────────────┐               │
│  │Middlewares│            │ PostgreSQL │               │
│  └───────────┘            └────────────┘               │
└─────────────────────────────────────────────────────────┘

Backend Structure

The backend follows the Model-View-Controller (MVC) pattern with additional middleware and helper layers.

Application Entry Point

The application is initialized in two main files:
import app from './app.js';
import 'dotenv/config';

const port = process.env.PORT;

app.listen(port, () => {
  console.log(`API funcionando en el puerto ${port}.`);
});
All API endpoints are prefixed with /api-productos. For example, to access products, use /api-productos/productos.

Routing Structure

The application uses a centralized routing system:
server/routes/index.js
import { Router } from "express";
import CategoriasRoutes from "./categorias.routes.js";
import ProductosRoutes from './productos.routes.js';
import UsuariosRoutes from './usuarios.routes.js';

const router = Router();

// Health check endpoint
router.get('/', (req, res) => {
  res.send('API funcionando correctamente.');
});

// Resource routes
router.use('/categorias', CategoriasRoutes);
router.use('/productos', ProductosRoutes);
router.use('/usuarios', UsuariosRoutes);

export default router;

API Endpoint Structure

All product endpoints require authentication via JWT token.
GET    /api-productos/productos      // List all products
POST   /api-productos/productos      // Create new product
GET    /api-productos/productos/:id  // Get product by ID
PUT    /api-productos/productos/:id  // Update product
DELETE /api-productos/productos/:id  // Delete product
Example route definition from server/routes/productos.routes.js:
import express from 'express';
import ProductosController from '../controllers/productos.controller.js';
import { auntenticarToken } from '../middlewares/auth.js';

const ProductosRoutes = express.Router();

ProductosRoutes.get('/', auntenticarToken, ProductosController.listar);
ProductosRoutes.post('/', auntenticarToken, ProductosController.crear);
ProductosRoutes.route('/:id')
  .get(auntenticarToken, ProductosController.leer)
  .put(auntenticarToken, ProductosController.actualizar)
  .delete(auntenticarToken, ProductosController.eliminar);
All category endpoints require authentication.
GET    /api-productos/categorias      // List all categories
POST   /api-productos/categorias      // Create new category
GET    /api-productos/categorias/:id  // Get category by ID
PUT    /api-productos/categorias/:id  // Update category
DELETE /api-productos/categorias/:id  // Delete category
User management and authentication endpoints.
GET    /api-productos/usuarios           // List all users (admin only)
POST   /api-productos/usuarios           // Create new user (admin only)
GET    /api-productos/usuarios/:id       // Get user by ID
PUT    /api-productos/usuarios/:id       // Update user
DELETE /api-productos/usuarios/:id       // Delete user
POST   /api-productos/usuarios/login     // User login
POST   /api-productos/usuarios/logout    // User logout
POST   /api-productos/usuarios/recuperar // Password recovery

MVC Layer Organization

1

Routes Layer

Routes define the API endpoints and apply middleware. Located in server/routes/.Each route file:
  • Maps HTTP methods to controller actions
  • Applies authentication middleware
  • Groups related endpoints
2

Controllers Layer

Controllers handle request/response logic. Located in server/controllers/.Responsibilities:
  • Request validation
  • Business logic coordination
  • Error handling
  • Response formatting
3

Models Layer

Models interact with the database. Located in server/models/.Example from server/models/productos.model.js:
import pool from '../config/database.js';

class Productos {
  async listar() {
    const sql = 'SELECT * FROM ProductosView ORDER BY nombre;';
    const resultado = await pool.query(sql);
    return resultado.rows;
  }

  async crear(producto) {
    const sql = 'INSERT INTO Productos (nombre, precio, stock, descripcion, idCategoria, creado) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP);';
    const resultado = await pool.query(sql, producto);
    return resultado.rowCount === 1;
  }

  async leer(id) {
    const sql = 'SELECT * FROM ProductosView WHERE id = $1;';
    const resultado = await pool.query(sql, [ id ]);
    return resultado.rows[0];
  }

  async actualizar(producto) {
    const sql = 'UPDATE Productos SET nombre = $1, precio = $2, stock = $3, descripcion = $4, idCategoria = $5, actualizado = CURRENT_TIMESTAMP WHERE id = $6;';
    const resultado = await pool.query(sql, producto);
    return resultado.rowCount === 1;
  }

  async eliminar(id) {
    const sql = 'DELETE FROM Productos WHERE id = $1;';
    const resultado = await pool.query(sql, [ id ]);
    return resultado.rowCount === 1;
  }
}

export default new Productos();

Database Connection

The application uses pg (node-postgres) for PostgreSQL connectivity:
server/config/database.js
import { Pool } from 'pg';
import 'dotenv/config';

const pool = new Pool({
  user: process.env.PG_USER || 'postgres',
  host: process.env.PG_HOST || 'localhost',
  database: process.env.PG_DATABASE || 'ejercicio_productos',
  password: process.env.PG_PASSWORD || 'tu_contraseña',
  port: process.env.PG_PORT || 5432,
});

pool.connect((error) => {
  if(error) throw error;
  console.log('Base de datos conectada.');
});

export default pool;
The pool instance is shared across all models. Ensure proper connection pooling configuration in production environments.

Frontend Structure

The frontend is built with Vue.js 3 and follows a component-based architecture.

Application Structure

src/
├── App.vue              # Root component
├── main.js              # Application entry point
├── router/              # Vue Router configuration
├── api/
│   └── axios.js         # Axios instance configuration
├── views/               # Page components
│   ├── Inicio.vue       # Home/Dashboard
│   ├── Productos.vue    # Products management
│   ├── Categorias.vue   # Categories management
│   └── Usuarios.vue     # User management
└── components/          # Reusable components
    ├── Header.vue
    ├── LoginContrasena.vue
    ├── Productos/
    ├── Categorias/
    └── Usuarios/

Communication Flow

1

User Interaction

User interacts with Vue components in the browser
2

API Request

Component calls API methods using Axios configured in src/api/axios.js
3

Authentication

Axios sends JWT token in cookies with each request
4

Backend Processing

Express routes → Middleware → Controllers → Models → Database
5

Response

Data flows back through the same chain to the Vue component

Middleware Architecture

The application uses middleware for cross-cutting concerns:

CORS

Configured in app.js to allow requests from the Vue.js development server (localhost:5173) with credentials.

Cookie Parser

Parses cookie headers to enable JWT token storage in HTTP-only cookies.

JSON Parser

Built-in Express middleware to parse JSON request bodies.

Authentication

Custom middleware for JWT verification and role-based access control. See Authentication for details.

Helper Modules

The backend includes several helper modules in server/helpers/:
  • auth.js - JWT token generation
  • respuestas.js - Standardized API response formatting
  • generarContraseña.js - Random password generation for recovery
  • mailer.js - Email sending functionality

Environment Configuration

The application uses environment variables for configuration:
.env
# Server Configuration
PORT=3000

# Database Configuration
PG_USER=postgres
PG_HOST=localhost
PG_DATABASE=ejercicio_productos
PG_PASSWORD=your_password
PG_PORT=5432

# JWT Configuration
JWT_SECRET=your_secret_key_here

# Email Configuration (for password recovery)
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_USER=[email protected]
EMAIL_PASSWORD=your_email_password
Always use strong, unique values for JWT_SECRET in production environments.

Data Flow Example

Here’s how a typical product creation request flows through the system:

Security Considerations

The architecture implements several security best practices:
  • JWT tokens stored in HTTP-only cookies to prevent XSS attacks
  • CORS configured to allow only specific origins
  • Passwords hashed with bcrypt before storage
  • All routes (except login/logout) require authentication
  • Role-based access control for admin operations
See Authentication for detailed security implementation.

Next Steps

Authentication

Learn about JWT authentication and authorization

Database Schema

Explore the PostgreSQL schema and data models

Build docs developers (and LLMs) love