CRUD: la base de la gestión de datos
El concepto CRUD está estrechamente vinculado a la gestión de datos digitales. CRUD hace referencia a un acrónimo en el que se reúnen las primeras letras de las cuatro operaciones fundamentales de aplicaciones persistentes en sistemas de bases de datos:
-
Create (Crear registros)
-
Read bzw. Retrieve (Leer registros)
-
Update (Actualizar registros)
-
Delete bzw. Destroy (Borrar registros)
En pocas palabras, CRUD resume las funciones requeridas por un usuario para crear y gestionar datos. Varios procesos de gestión de datos están basados en CRUD, en los que dichas operaciones están específicamente adaptadas a los requisitos del sistema y de usuario, ya sea para la gestión de bases de datos o para el uso de aplicaciones. Para los expertos, las operaciones son las herramientas de acceso típicas e indispensables para comprobar, por ejemplo, los problemas de la base de datos, mientras que para los usuarios, CRUD significa crear una cuenta (créate) y utilizarla (read), actualizarla (update) o borrarla (delete) en cualquier momento. Dependiendo de la configuración regional, las operaciones CRUD pueden implementarse de diferentes maneras, como lo muestra la siguiente tabla:
| CRUD-Operation | SQL | RESTful HTTP | XQuery |
|---|---|---|---|
| Create | INSERT | POST, PUT | insert |
| Read | SELECT | GET, HEAD | copy/modify/return |
| Update | UPDATE | PUT, PATCH | replace, rename |
| Delete | DELETE | DELETE | delete |
Debemos crear los 6 metodos por cada modelo segun demande los requerimientos del aplicativo.
-
Buscar todos los registros
-
Buscar por Id
-
Crear o Insertar un Registro
-
Actualizar o Cambiar el valor de uno o varios registos
-
Eliminar fisicamente uno o varios registros
-
Eliminar Logicamente uno o varios registros
Para ello se realizan los controladores y las rutas de de cada modelo, al igual que las configuraciones de la rutas de la siguiente manera:
Paso 1: Controladores
1.1 Controladores de los modelos de Autorizacion
1.1.1 Controlador User
src/controllers/Authorization/user.controller.ts
import { Request, Response } from 'express';
import { User, UserI } from '../../models/authorization/User';
export class UserController {
public async getAllUsers(req: Request, res: Response): Promise<void> {
try {
const users: UserI[] = await User.findAll();
res.status(200).json({ users });
} catch (error) {
res.status(500).json({ error: 'Error al obtener los usuarios' });
}
}
}
1.1.2 Controlador Role
src/controllers/Authorization/role.controller.ts
import { Request, Response } from 'express';
import { Role, RoleI } from '../../models/authorization/Role';
export class RoleController {
// Obtener todos los roles
public async getAllRoles(req: Request, res: Response): Promise<void> {
try {
const roles: RoleI[] = await Role.findAll();
res.status(200).json({ roles });
} catch (error) {
res.status(500).json({ error: 'Error al obtener los roles' });
}
}
// Obtener un rol por ID
public async getRoleById(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
if (role) {
res.status(200).json(role);
} else {
res.status(404).json({ error: 'Rol no encontrado o inactivo' });
}
} catch (error) {
res.status(500).json({ error: 'Error al obtener el rol' });
}
}
// Crear un nuevo rol
public async createRole(req: Request, res: Response): Promise<void> {
const { name, is_active } = req.body;
try {
const newRole = await Role.create({ name, is_active });
res.status(201).json(newRole);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Actualizar un rol
public async updateRole(req: Request, res: Response): Promise<void> {
const { id } = req.params;
const { name, is_active } = req.body;
try {
const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
if (role) {
await role.update({ name, is_active });
res.status(200).json(role);
} else {
res.status(404).json({ error: 'Rol no encontrado o inactivo' });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Eliminar un rol físicamente
public async deleteRole(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const role = await Role.findByPk(id);
if (role) {
await role.destroy();
res.status(200).json({ message: 'Rol eliminado correctamente' });
} else {
res.status(404).json({ error: 'Rol no encontrado' });
}
} catch (error) {
res.status(500).json({ error: 'Error al eliminar el rol' });
}
}
// Eliminar un rol lógicamente
public async deleteRoleAdv(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
if (role) {
await role.update({ is_active: "INACTIVE" });
res.status(200).json({ message: 'Rol marcado como inactivo' });
} else {
res.status(404).json({ error: 'Rol no encontrado' });
}
} catch (error) {
res.status(500).json({ error: 'Error al marcar el rol como inactivo' });
}
}
}
1.1.3 Controlador RoleUser
src/controllers/Authorization/role_user.controller.ts
import { Request, Response } from 'express';
import { RoleUser, RoleUserI } from '../../models/authorization/RoleUser';
export class RoleUserController {
// Obtener todos los RoleUsers
public async getAllRoleUsers(req: Request, res: Response): Promise<void> {
try {
const roleUsers: RoleUserI[] = await RoleUser.findAll();
res.status(200).json({ roleUsers });
} catch (error) {
res.status(500).json({ error: 'Error al obtener los usuarios de roles' });
}
}
// Obtener un RoleUser por ID
public async getRoleUserById(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
if (roleUser) {
res.status(200).json(roleUser);
} else {
res.status(404).json({ error: 'RoleUser no encontrado o inactivo' });
}
} catch (error) {
res.status(500).json({ error: 'Error al obtener el RoleUser' });
}
}
// Crear un nuevo RoleUser
public async createRoleUser(req: Request, res: Response): Promise<void> {
const { role_id, user_id, is_active } = req.body;
try {
const newRoleUser = await RoleUser.create({ role_id, user_id, is_active });
res.status(201).json(newRoleUser);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Actualizar un RoleUser
public async updateRoleUser(req: Request, res: Response): Promise<void> {
const { id } = req.params;
const { role_id, user_id, is_active } = req.body;
try {
const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
if (roleUser) {
await roleUser.update({ role_id, user_id, is_active });
res.status(200).json(roleUser);
} else {
res.status(404).json({ error: 'RoleUser no encontrado o inactivo' });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Eliminar un RoleUser físicamente
public async deleteRoleUser(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const roleUser = await RoleUser.findByPk(id);
if (roleUser) {
await roleUser.destroy();
res.status(200).json({ message: 'RoleUser eliminado correctamente' });
} else {
res.status(404).json({ error: 'RoleUser no encontrado' });
}
} catch (error) {
res.status(500).json({ error: 'Error al eliminar el RoleUser' });
}
}
// Eliminar un RoleUser lógicamente
public async deleteRoleUserAdv(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
if (roleUser) {
await roleUser.update({ is_active: "INACTIVE" });
res.status(200).json({ message: 'RoleUser marcado como inactivo' });
} else {
res.status(404).json({ error: 'RoleUser no encontrado' });
}
} catch (error) {
res.status(500).json({ error: 'Error al marcar el RoleUser como inactivo' });
}
}
}
1.1.4 Controlador Resource
src/controllers/Authorization/resource.controller.ts
import { Request, Response } from "express";
import { Resource, ResourceI } from "../../models/authorization/Resource";
export class ResourceController {
// Obtener todos los recursos
public async getAllResources(req: Request, res: Response): Promise<void> {
try {
const resources: ResourceI[] = await Resource.findAll({
where: { is_active: "ACTIVE" },
});
res.status(200).json({ resources });
} catch (error) {
res.status(500).json({ error: "Error al obtener los recursos" });
}
}
// Obtener un recurso por ID
public async getResourceById(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const resource = await Resource.findOne({
where: { id, is_active: "ACTIVE" },
});
if (resource) {
res.status(200).json(resource);
} else {
res.status(404).json({ error: "Recurso no encontrado o inactivo" });
}
} catch (error) {
res.status(500).json({ error: "Error al obtener el recurso" });
}
}
// Crear un nuevo recurso
public async createResource(req: Request, res: Response): Promise<void> {
const { path, method, is_active } = req.body;
try {
const newResource = await Resource.create({ path, method, is_active });
res.status(201).json(newResource);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Actualizar un recurso
public async updateResource(req: Request, res: Response): Promise<void> {
const { id } = req.params;
const { path, method, is_active } = req.body;
try {
const resource = await Resource.findOne({ where: { id, is_active: "ACTIVE" } });
if (resource) {
await resource.update({ path, method, is_active });
res.status(200).json(resource);
} else {
res.status(404).json({ error: "Recurso no encontrado o inactivo" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Eliminar un recurso físicamente
public async deleteResource(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const resource = await Resource.findByPk(id);
if (resource) {
await resource.destroy();
res.status(200).json({ message: "Recurso eliminado correctamente" });
} else {
res.status(404).json({ error: "Recurso no encontrado" });
}
} catch (error) {
res.status(500).json({ error: "Error al eliminar el recurso" });
}
}
// Eliminar un recurso lógicamente
public async deleteResourceAdv(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const resource = await Resource.findOne({ where: { id, is_active: "ACTIVE" } });
if (resource) {
await resource.update({ is_active: "INACTIVE" });
res.status(200).json({ message: "Recurso marcado como inactivo" });
} else {
res.status(404).json({ error: "Recurso no encontrado" });
}
} catch (error) {
res.status(500).json({ error: "Error al marcar el recurso como inactivo" });
}
}
}
1.1.5 Controlador ResourceRole
src/controllers/Authorization/resourceRole.controller.ts
import { Request, Response } from "express";
import { ResourceRole, ResourceRoleI } from "../../models/authorization/ResourceRole";
export class ResourceRoleController {
// Obtener todos los ResourceRoles
public async getAllResourceRoles(req: Request, res: Response): Promise<void> {
try {
const resourceRoles: ResourceRoleI[] = await ResourceRole.findAll({
where: { is_active: "ACTIVE" },
});
res.status(200).json({ resourceRoles });
} catch (error) {
res.status(500).json({ error: "Error al obtener los ResourceRoles" });
}
}
// Obtener un ResourceRole por ID
public async getResourceRoleById(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const resourceRole = await ResourceRole.findOne({
where: { id, is_active: "ACTIVE" },
});
if (resourceRole) {
res.status(200).json(resourceRole);
} else {
res.status(404).json({ error: "ResourceRole no encontrado o inactivo" });
}
} catch (error) {
res.status(500).json({ error: "Error al obtener el ResourceRole" });
}
}
// Crear un nuevo ResourceRole
public async createResourceRole(req: Request, res: Response): Promise<void> {
const { resource_id, role_id, is_active } = req.body;
try {
const newResourceRole = await ResourceRole.create({ resource_id, role_id, is_active });
res.status(201).json(newResourceRole);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Actualizar un ResourceRole
public async updateResourceRole(req: Request, res: Response): Promise<void> {
const { id } = req.params;
const { resource_id, role_id, is_active } = req.body;
try {
const resourceRole = await ResourceRole.findOne({ where: { id, is_active: "ACTIVE" } });
if (resourceRole) {
await resourceRole.update({ resource_id, role_id, is_active });
res.status(200).json(resourceRole);
} else {
res.status(404).json({ error: "ResourceRole no encontrado o inactivo" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Eliminar un ResourceRole físicamente
public async deleteResourceRole(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const resourceRole = await ResourceRole.findByPk(id);
if (resourceRole) {
await resourceRole.destroy();
res.status(200).json({ message: "ResourceRole eliminado correctamente" });
} else {
res.status(404).json({ error: "ResourceRole no encontrado" });
}
} catch (error) {
res.status(500).json({ error: "Error al eliminar el ResourceRole" });
}
}
// Eliminar un ResourceRole lógicamente
public async deleteResourceRoleAdv(req: Request, res: Response): Promise<void> {
const { id } = req.params;
try {
const resourceRole = await ResourceRole.findOne({ where: { id, is_active: "ACTIVE" } });
if (resourceRole) {
await resourceRole.update({ is_active: "INACTIVE" });
res.status(200).json({ message: "ResourceRole marcado como inactivo" });
} else {
res.status(404).json({ error: "ResourceRole no encontrado" });
}
} catch (error) {
res.status(500).json({ error: "Error al marcar el ResourceRole como inactivo" });
}
}
}
1.1.6 Controlador RefreshToken
src/controllers/Authorization/refresh_token.controller.ts
import { Request, Response } from 'express';
import { RefreshToken, RefreshTokenI } from '../../models/authorization/RefreshToken';
export class RefreshTokenController {
public async getAllRefreshToken(req: Request, res: Response): Promise<void> {
try {
const referes_tokens: RefreshTokenI[] = await RefreshToken.findAll();
res.status(200).json({ referes_tokens });
} catch (error) {
res.status(500).json({ error: 'Error al obtener los los refresh token' });
}
}
}
1.2 Controladores de los modelos de Negocio
1.2.1 Controlador Client
src/controllers/client.controller.ts
import { Request, Response } from "express";
import { Client, ClientI } from "../models/Client";
export class ClientController {
// Get all clients with status "ACTIVE"
public async getAllClients(req: Request, res: Response) {
try {
const clients: ClientI[] = await Client.findAll({
where: { status: 'ACTIVE' },
});
res.status(200).json({ clients });
} catch (error) {
res.status(500).json({ error: "Error fetching clients" });
}
}
// Get a client by ID
public async getClientById(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const client = await Client.findOne({
where: {
id: pk,
status: 'ACTIVE' },
});
if (client) {
res.status(200).json({client});
} else {
res.status(404).json({ error: "Client not found or inactive" });
}
} catch (error) {
res.status(500).json({ error: "Error fetching client" });
}
}
// Create a new client
public async createClient(req: Request, res: Response) {
const { id, name, address, phone, email, password, status } = req.body;
try {
let body: ClientI = {
name,
address,
phone,
email,
password,
status,
};
const newClient = await Client.create({ ...body });
res.status(201).json(newClient);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Update a client
public async updateClient(req: Request, res: Response) {
const { id: pk } = req.params;
const { id, name, address, phone, email, password, status } = req.body;
try {
let body: ClientI = {
name,
address,
phone,
email,
password,
status,
};
const clientExist = await Client.findOne({
where: {
id: pk,
status: 'ACTIVE' },
});
if (clientExist) {
await clientExist.update(body, {
where: { id: pk },
});
res.status(200).json(clientExist);
} else {
res.status(404).json({ error: "Client not found or inactive" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Delete a client physically
public async deleteClient(req: Request, res: Response) {
try {
const { id } = req.params;
const clientToDelete = await Client.findByPk(id);
if (clientToDelete) {
await clientToDelete.destroy();
res.status(200).json({ message: "Client deleted successfully" });
} else {
res.status(404).json({ error: "Client not found" });
}
} catch (error) {
res.status(500).json({ error: "Error deleting client" });
}
}
// Delete a client logically (change status to "INACTIVE")
public async deleteClientAdv(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const clientToUpdate = await Client.findOne({
where: {
id: pk,
status: 'ACTIVE' },
});
if (clientToUpdate) {
await clientToUpdate.update({ status: 'INACTIVE' });
res.status(200).json({ message: "Client marked as inactive" });
} else {
res.status(404).json({ error: "Client not found" });
}
} catch (error) {
res.status(500).json({ error: "Error marking client as inactive" });
}
}
}
1.2.2 Controlador ProductType
src/controllers/productType.controller.ts
import { Request, Response } from "express";
import { ProductType, ProductTypeI } from "../models/ProductType";
export class ProductTypeController {
// Get all product types
public async getAllProductTypes(req: Request, res: Response) {
try {
const productTypes: ProductTypeI[] = await ProductType.findAll({
where: { status: true },
});
res.status(200).json({ productTypes });
} catch (error) {
res.status(500).json({ error: "Error fetching product types" });
}
}
// Get a product type by ID
public async getProductTypeById(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const productType = await ProductType.findOne({
where: { id: pk, status: true },
});
if (productType) {
res.status(200).json(productType);
} else {
res.status(404).json({ error: "Product type not found or inactive" });
}
} catch (error) {
res.status(500).json({ error: "Error fetching product type" });
}
}
// Create a new product type
public async createProductType(req: Request, res: Response) {
const { name, description, status } = req.body;
try {
let body: ProductTypeI = {
name,
description,
status,
};
const newProductType = await ProductType.create({ ...body });
res.status(201).json(newProductType);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Update a product type
public async updateProductType(req: Request, res: Response) {
const { id: pk } = req.params;
const { name, description, status } = req.body;
try {
let body: ProductTypeI = {
name,
description,
status,
};
const productTypeExist = await ProductType.findOne({
where: { id: pk, status: true },
});
if (productTypeExist) {
await productTypeExist.update(body, {
where: { id: pk },
});
res.status(200).json(productTypeExist);
} else {
res.status(404).json({ error: "Product type not found or inactive" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Delete a product type physically
public async deleteProductType(req: Request, res: Response) {
try {
const { id } = req.params;
const productTypeToDelete = await ProductType.findByPk(id);
if (productTypeToDelete) {
await productTypeToDelete.destroy();
res.status(200).json({ message: "Product type deleted successfully" });
} else {
res.status(404).json({ error: "Product type not found" });
}
} catch (error) {
res.status(500).json({ error: "Error deleting product type" });
}
}
// Delete a product type logically (change status to false)
public async deleteProductTypeAdv(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const productTypeToUpdate = await ProductType.findOne({
where: { id: pk, status: true },
});
if (productTypeToUpdate) {
await productTypeToUpdate.update({ status: false });
res.status(200).json({ message: "Product type marked as inactive" });
} else {
res.status(404).json({ error: "Product type not found" });
}
} catch (error) {
res.status(500).json({ error: "Error marking product type as inactive" });
}
}
}
1.2.3 Controlador Product
src/controllers/product.controller.ts
import { Request, Response } from "express";
import { Product, ProductI } from "../models/Product";
export class ProductController {
// Get all products with status "ACTIVE"
public async getAllProducts(req: Request, res: Response) {
try {
const products: ProductI[] = await Product.findAll({
where: { status: "ACTIVE" },
});
res.status(200).json({ products });
} catch (error) {
res.status(500).json({ error: "Error fetching products" });
}
}
// Get a product by ID
public async getProductById(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const product = await Product.findOne({
where: { id: pk, status: "ACTIVE" },
});
if (product) {
res.status(200).json(product);
} else {
res.status(404).json({ error: "Product not found or inactive" });
}
} catch (error) {
res.status(500).json({ error: "Error fetching product" });
}
}
// Create a new product
public async createProduct(req: Request, res: Response) {
const { id, name, brand, price, min_stock, quantity, product_type_id,status } = req.body;
try {
let body: ProductI = {
name,
brand,
price,
min_stock,
quantity,
product_type_id,
status,
};
const newProduct = await Product.create({ ...body });
res.status(201).json(newProduct);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Update a product
public async updateProduct(req: Request, res: Response) {
const { id: pk } = req.params;
const { id,name, brand, price, min_stock, quantity, product_type_id, status } = req.body;
try {
let body: ProductI = {
name,
brand,
price,
min_stock,
quantity,
product_type_id,
status,
};
const productExist = await Product.findOne({
where: { id: pk, status: "ACTIVE" },
});
if (productExist) {
await productExist.update(body, {
where: { id: pk },
});
res.status(200).json(productExist);
} else {
res.status(404).json({ error: "Product not found or inactive" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Delete a product physically
public async deleteProduct(req: Request, res: Response) {
try {
const { id } = req.params;
const productToDelete = await Product.findByPk(id);
if (productToDelete) {
await productToDelete.destroy();
res.status(200).json({ message: "Product deleted successfully" });
} else {
res.status(404).json({ error: "Product not found" });
}
} catch (error) {
res.status(500).json({ error: "Error deleting product" });
}
}
// Delete a product logically (change status to "INACTIVE")
public async deleteProductAdv(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const productToUpdate = await Product.findOne({
where: { id: pk, status: "ACTIVE" },
});
if (productToUpdate) {
await productToUpdate.update({ status: "INACTIVE" });
res.status(200).json({ message: "Product marked as inactive" });
} else {
res.status(404).json({ error: "Product not found" });
}
} catch (error) {
res.status(500).json({ error: "Error marking product as inactive" });
}
}
}
1.2.4 Controlador Sale
src/controllers/sale.controller.ts
import { Request, Response } from "express";
import { Sale, SaleI } from "../models/Sale";
export class SaleController {
// Get all sales with status "ACTIVE"
public async getAllSales(req: Request, res: Response) {
try {
const sales: SaleI[] = await Sale.findAll({
where: { status: "ACTIVE" },
});
res.status(200).json({ sales });
} catch (error) {
res.status(500).json({ error: "Error fetching sales" });
}
}
// Get a sale by ID
public async getSaleById(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const sale = await Sale.findOne({
where: {
id: pk,
status: "ACTIVE" },
});
if (sale) {
res.status(200).json(sale);
} else {
res.status(404).json({ error: "Sale not found or inactive" });
}
} catch (error) {
res.status(500).json({ error: "Error fetching sale" });
}
}
// Create a new sale
public async createSale(req: Request, res: Response) {
const { id, sale_date, subtotal, tax, discounts, total, status, client_id } = req.body;
try {
let body: SaleI = {
sale_date,
subtotal,
tax,
discounts,
total,
status,
client_id,
};
console.log(body)
const newSale = await Sale.create({ ...body });
res.status(201).json(newSale);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Update a sale
public async updateSale(req: Request, res: Response) {
const { id: pk } = req.params;
const { id, sale_date, subtotal, tax, discounts, total, status, client_id } = req.body;
try {
let body: SaleI = {
sale_date,
subtotal,
tax,
discounts,
total,
status,
client_id,
};
const saleExist = await Sale.findOne({
where: {
id: pk,
status: "ACTIVE" },
});
if (saleExist) {
await saleExist.update(body, {
where: { id: pk },
});
res.status(200).json(saleExist);
} else {
res.status(404).json({ error: "Sale not found or inactive" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Delete a sale physically
public async deleteSale(req: Request, res: Response) {
try {
const { id } = req.params;
const saleToDelete = await Sale.findByPk(id);
if (saleToDelete) {
await saleToDelete.destroy();
res.status(200).json({ message: "Sale deleted successfully" });
} else {
res.status(404).json({ error: "Sale not found" });
}
} catch (error) {
res.status(500).json({ error: "Error deleting sale" });
}
}
// Delete a sale logically (change status to "INACTIVE")
public async deleteSaleAdv(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const saleToUpdate = await Sale.findOne({
where: {
id: pk,
status: "ACTIVE" },
});
if (saleToUpdate) {
await saleToUpdate.update({ status: "INACTIVE" });
res.status(200).json({ message: "Sale marked as inactive" });
} else {
res.status(404).json({ error: "Sale not found" });
}
} catch (error) {
res.status(500).json({ error: "Error marking sale as inactive" });
}
}
}
1.2.5 Controlador ProductSale
src/controllers/productSale.controller.ts
import { Request, Response } from "express";
import { ProductSale, ProductSaleI } from "../models/ProductSale";
export class ProductSaleController {
// Get all product sales
public async getAllProductSales(req: Request, res: Response) {
try {
const productSales: ProductSaleI[] = await ProductSale.findAll({
where: { status: true },
});
res.status(200).json({ productSales });
} catch (error) {
res.status(500).json({ error: "Error fetching product sales" });
}
}
// Get a product sale by ID
public async getProductSaleById(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const productSale = await ProductSale.findOne({
where: { id: pk, status: true },
});
if (productSale) {
res.status(200).json(productSale);
} else {
res.status(404).json({ error: "Product sale not found or inactive" });
}
} catch (error) {
res.status(500).json({ error: "Error fetching product sale" });
}
}
// Create a new product sale
public async createProductSale(req: Request, res: Response) {
const { total, sale_id, product_id } = req.body;
try {
let body: ProductSaleI = {
total,
sale_id,
product_id,
};
const newProductSale = await ProductSale.create({ ...body });
res.status(201).json(newProductSale);
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Update a product sale
public async updateProductSale(req: Request, res: Response) {
const { id: pk } = req.params;
const { total, sale_id, product_id } = req.body;
try {
let body: ProductSaleI = {
total,
sale_id,
product_id,
};
const productSaleExist = await ProductSale.findOne({
where: { id: pk, status: true },
});
if (productSaleExist) {
await productSaleExist.update(body, {
where: { id: pk },
});
res.status(200).json(productSaleExist);
} else {
res.status(404).json({ error: "Product sale not found or inactive" });
}
} catch (error: any) {
res.status(400).json({ error: error.message });
}
}
// Delete a product sale physically
public async deleteProductSale(req: Request, res: Response) {
try {
const { id } = req.params;
const productSaleToDelete = await ProductSale.findByPk(id);
if (productSaleToDelete) {
await productSaleToDelete.destroy();
res.status(200).json({ message: "Product sale deleted successfully" });
} else {
res.status(404).json({ error: "Product sale not found" });
}
} catch (error) {
res.status(500).json({ error: "Error deleting product sale" });
}
}
// Delete a product sale logically (change status to false)
public async deleteProductSaleAdv(req: Request, res: Response) {
try {
const { id: pk } = req.params;
const productSaleToUpdate = await ProductSale.findOne({
where: { id: pk, status: true },
});
if (productSaleToUpdate) {
await productSaleToUpdate.update({ status: false });
res.status(200).json({ message: "Product sale marked as inactive" });
} else {
res.status(404).json({ error: "Product sale not found" });
}
} catch (error) {
res.status(500).json({ error: "Error marking product sale as inactive" });
}
}
}
Paso 2: Rutas
2.1 Rutas de los modelos de Autorizacion
1.2.1 Rutas de User
src/routes/authorization/user.ts
import { Application } from "express";
import { UserController } from '../../controllers/authorization/user.controller';
export class UserRoutes {
public userController: UserController = new UserController();
public routes(app: Application): void {
app.route("/api/users").get(this.userController.getAllUsers);
app.route("/api/users").post(this.userController.getAllUsers);
}
}
1.2.2 Rutas de Role
src/routes/authorization/role.ts
import { Application } from "express";
import { RoleController } from '../../controllers/authorization/role.controller';
export class RoleRoutes {
public roleController: RoleController = new RoleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/roles")
.get(this.roleController.getAllRoles)
.post(this.roleController.createRole);
app.route("/api/roles/:id")
.get(this.roleController.getRoleById)
.patch(this.roleController.updateRole)
.delete(this.roleController.deleteRole);
app.route("/api/roles/:id/logic")
.delete(this.roleController.deleteRoleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
1.2.3 Rutas de RoleUser
src/routes/authorization/role_user.ts
import { Application } from "express";
import { RoleUserController } from '../../controllers/authorization/role_user.controller';
export class RoleUserRoutes {
public roleUserController: RoleUserController = new RoleUserController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/roleUsers")
.get(this.roleUserController.getAllRoleUsers)
.post(this.roleUserController.createRoleUser);
app.route("/api/roleUsers/:id")
.get(this.roleUserController.getRoleUserById)
.patch(this.roleUserController.updateRoleUser)
.delete(this.roleUserController.deleteRoleUser);
app.route("/api/roleUsers/:id/logic")
.delete(this.roleUserController.deleteRoleUserAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
}
}
1.2.4 Rutas de Resource
src/routes/authorization/resource.ts
import { Application } from "express";
import { ResourceController } from "../../controllers/authorization/resource.controller";
export class ResourceRoutes {
public resourceController: ResourceController = new ResourceController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/resources")
.get(this.resourceController.getAllResources)
.post(this.resourceController.createResource);
app.route("/api/resources/:id")
.get(this.resourceController.getResourceById)
.patch(this.resourceController.updateResource)
.delete(this.resourceController.deleteResource);
app.route("/api/resources/:id/logic")
.delete(this.resourceController.deleteResourceAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
1.2.5 Rutas de ResourceRole
src/routes/authorization/resourceRole.ts
import { Application } from "express";
import { ResourceRoleController } from "../../controllers/authorization/resourceRole.controller";
export class ResourceRoleRoutes {
public resourceRoleController: ResourceRoleController = new ResourceRoleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/resource-roles")
.get(this.resourceRoleController.getAllResourceRoles)
.post(this.resourceRoleController.createResourceRole);
app.route("/api/resource-roles/:id")
.get(this.resourceRoleController.getResourceRoleById)
.patch(this.resourceRoleController.updateResourceRole)
.delete(this.resourceRoleController.deleteResourceRole);
app.route("/api/resource-roles/:id/logic")
.delete(this.resourceRoleController.deleteResourceRoleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
1.2.6 Rutas de Refreshtoken
src/routes/authorization/refresh_token.ts
import { Application } from "express";
import { RefreshTokenController } from "../../controllers/authorization/refres_token.controller";
export class RefreshTokenRoutes {
public refreshTokenController: RefreshTokenController = new RefreshTokenController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/refresk-token")
.get(this.refreshTokenController.getAllRefreshToken);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
2.2 Rutas de los modelos de Negocio
2.2.1 Rutas de Client
src/routes/client.ts
import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";
export class ClientRoutes {
public clientController: ClientController = new ClientController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/clientes")
.get(this.clientController.getAllClients)
.post(this.clientController.createClient);
app.route("/api/clientes/:id")
.get(this.clientController.getClientById)
.patch(this.clientController.updateClient)
.delete(this.clientController.deleteClient);
app.route("/api/clientes/:id/logic")
.delete(this.clientController.deleteClientAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
}
}
2.2.2 Rutas de ProductType
src/routes/productType.ts
import { Application } from "express";
import { ProductTypeController } from "../controllers/productType.controller";
export class ProductTypeRoutes {
public productTypeController: ProductTypeController = new ProductTypeController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/product-types")
.get(this.productTypeController.getAllProductTypes)
.post(this.productTypeController.createProductType);
app.route("/product-types/:id")
.get(this.productTypeController.getProductTypeById)
.patch(this.productTypeController.updateProductType)
.delete(this.productTypeController.deleteProductType);
app.route("/product-types/:id/logic")
.delete(this.productTypeController.deleteProductTypeAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
2.2.3 Rutas de Product
src/routes/product.ts
import { Application } from "express";
import { ProductController } from "../controllers/product.controller";
export class ProductRoutes {
public productController: ProductController = new ProductController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/products")
.get(this.productController.getAllProducts)
.post(this.productController.createProduct);
app.route("/products/:id")
.get(this.productController.getProductById)
.patch(this.productController.updateProduct)
.delete(this.productController.deleteProduct);
app.route("/products/:id/logic")
.delete(this.productController.deleteProductAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
2.2.4 Rutas de Sale
src/routes/sale.ts
import { Application } from "express";
import { SaleController } from "../controllers/sale.controller";
export class SaleRoutes {
public saleController: SaleController = new SaleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/ventas")
.get(this.saleController.getAllSales)
.post(this.saleController.createSale);
app.route("/ventas/:id")
.get(this.saleController.getSaleById)
.patch(this.saleController.updateSale)
.delete(this.saleController.deleteSale);
app.route("/ventas/:id/logic")
.delete(this.saleController.deleteSaleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
2.2.5 Rutas de ProductSale
src/routes/productSale.ts
import { Application } from "express";
import { ProductSaleController } from "../controllers/productSale.controller";
export class ProductSaleRoutes {
public productSaleController: ProductSaleController = new ProductSaleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/product-sales")
.get(this.productSaleController.getAllProductSales)
.post(this.productSaleController.createProductSale);
app.route("/product-sales/:id")
.get(this.productSaleController.getProductSaleById)
.patch(this.productSaleController.updateProductSale)
.delete(this.productSaleController.deleteProductSale);
app.route("/product-sales/:id/logic")
.delete(this.productSaleController.deleteProductSaleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
// Si se requieren rutas protegidas, se pueden agregar aquí:
}
}
2.2 Agregar Rutas a Routes/Index
src/routes/index.ts
import { Router } from "express";
import { ClientRoutes } from "./client";
import { SaleRoutes } from "./sale";
import { ProductRoutes } from "./product";
import { ProductTypeRoutes } from "./productType";
import { ProductSaleRoutes } from "./productSale";
import { UserRoutes } from "./authorization/user";
import { RoleRoutes } from "./authorization/role";
import { RoleUserRoutes } from "./authorization/role_user";
import { RefreshTokenRoutes } from "./authorization/refresk_token";
import { ResourceRoutes } from "./authorization/resource"; // Import ResourceRoutes
import { ResourceRoleRoutes } from "./authorization/resourceRole"; // Import ResourceRoleRoutes
export class Routes {
public clientRoutes: ClientRoutes = new ClientRoutes();
public saleRoutes: SaleRoutes = new SaleRoutes();
public productRoutes: ProductRoutes = new ProductRoutes();
public productTypeRoutes: ProductTypeRoutes = new ProductTypeRoutes();
public productSaleRoutes: ProductSaleRoutes = new ProductSaleRoutes();
public userRoutes: UserRoutes = new UserRoutes();
public roleRoutes: RoleRoutes = new RoleRoutes();
public roleUserRoutes: RoleUserRoutes = new RoleUserRoutes();
public refreshTokenRoutes: RefreshTokenRoutes = new RefreshTokenRoutes();
public resourceRoutes: ResourceRoutes = new ResourceRoutes(); // Add ResourceRoutes
public resourceRoleRoutes: ResourceRoleRoutes = new ResourceRoleRoutes(); // Add ResourceRoutes
}
2.3 Agregar Rutas a Config/Index
src/config/index.ts
Agregar modulo de private routes() y su importacion
import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize, testConnection, getDatabaseInfo } from "../database/db";
import { Routes } from "../routes/index";
var cors = require("cors");
dotenv.config();
export class App {
public app: Application;
public routePrv: Routes = new Routes();
constructor(private port?: number | string) {
this.app = express();
this.settings();
this.middlewares();
this.routes();
this.dbConnection();
}
private settings(): void {
this.app.set('port', this.port || process.env.PORT || 4000);
}
private middlewares(): void {
this.app.use(morgan('dev'));
this.app.use(cors());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
}
// Route configuration
private routes(): void {
this.routePrv.clientRoutes.routes(this.app);
this.routePrv.saleRoutes.routes(this.app);
this.routePrv.productRoutes.routes(this.app);
this.routePrv.productTypeRoutes.routes(this.app);
this.routePrv.productSaleRoutes.routes(this.app);
this.routePrv.userRoutes.routes(this.app);
this.routePrv.roleRoutes.routes(this.app);
this.routePrv.roleUserRoutes.routes(this.app);
this.routePrv.refreshTokenRoutes.routes(this.app);
this.routePrv.resourceRoutes.routes(this.app);
this.routePrv.resourceRoleRoutes.routes(this.app);
}
private async dbConnection(): Promise<void> {
try {
// Mostrar información de la base de datos seleccionada
const dbInfo = getDatabaseInfo();
console.log(`🔗 Intentando conectar a: ${dbInfo.engine.toUpperCase()}`);
// Probar la conexión
const isConnected = await testConnection();
if (!isConnected) {
throw new Error(`No se pudo conectar a la base de datos ${dbInfo.engine.toUpperCase()}`);
}
// Sincronizar la base de datos
await sequelize.sync({ force: false });
console.log(`📦 Base de datos sincronizada exitosamente`);
} catch (error) {
console.error("❌ Error al conectar con la base de datos:", error);
process.exit(1); // Terminar la aplicación si no se puede conectar
}
}
async listen() {
await this.app.listen(this.app.get('port'));
console.log(`🚀 Servidor ejecutándose en puerto ${this.app.get('port')}`);
}
}
Luego,

2.4 Peticiones API REST
Para las peticiones API REST utilizaremos la extension en VS CODE RESTClient.
Instalar Extension

Luego, Crear carpeta src/http y crear archivo por modelo definidas en los archivos de rutas.
2.4.1 Http de los modelos de Autorizacion
2.4.1.1 Http de de User
src/http/authorization/user.http
### user.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### Esto es solo como ejemplo academico, dado que este modelo lo debemos manejar posteriormente con Auth
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los usuarios
GET {{baseUrl}}/api/users
### Crear usuario (usando el mismo endpoint)
POST {{baseUrl}}/api/users
Content-Type: application/json
{
"username": "newuser",
"email": "newuser@example.com",
"password": "123456",
"is_active": "ACTIVE",
"avatar": "avatar.jpg"
}
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.2 Http de de Role
src/http/authorization/role.http
### role.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles
GET {{baseUrl}}/api/roles
### Obtener rol por ID
GET {{baseUrl}}/api/roles/1
### Crear un nuevo rol
POST {{baseUrl}}/api/roles
Content-Type: application/json
{
"name": "Administrator",
"is_active": "ACTIVE"
}
### Actualizar un rol
PATCH {{baseUrl}}/api/roles/1
Content-Type: application/json
{
"name": "Super Administrator",
"is_active": "ACTIVE"
}
### Eliminar un rol físicamente
DELETE {{baseUrl}}/api/roles/1
### Eliminar un rol lógicamente
DELETE {{baseUrl}}/api/roles/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.3 Http de de RoleUser
src/http/authorization/role_user.http
### roleUser.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles de usuario
GET {{baseUrl}}/api/roleUsers
### Obtener rol de usuario por ID
GET {{baseUrl}}/api/roleUsers/1
### Crear un nuevo rol de usuario
POST {{baseUrl}}/api/roleUsers
Content-Type: application/json
{
"role_id": 1,
"user_id": 1,
"is_active": "ACTIVE"
}
### Actualizar un rol de usuario
PATCH {{baseUrl}}/api/roleUsers/1
Content-Type: application/json
{
"role_id": 2,
"user_id": 1,
"is_active": "ACTIVE"
}
### Eliminar un rol de usuario físicamente
DELETE {{baseUrl}}/api/roleUsers/1
### Eliminar un rol de usuario lógicamente
DELETE {{baseUrl}}/api/roleUsers/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.4 Http de de Resource
src/http/authorization/resource.http
### resource.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los recursos
GET {{baseUrl}}/api/resources
### Obtener recurso por ID
GET {{baseUrl}}/api/resources/1
### Crear un nuevo recurso
POST {{baseUrl}}/api/resources
Content-Type: application/json
{
"path": "/api/clientes",
"method": "GET",
"is_active": "ACTIVE"
}
### Actualizar un recurso
PATCH {{baseUrl}}/api/resources/1
Content-Type: application/json
{
"path": "/api/clientes/:id",
"method": "GET",
"is_active": "ACTIVE"
}
### Eliminar un recurso físicamente
DELETE {{baseUrl}}/api/resources/1
### Eliminar un recurso lógicamente
DELETE {{baseUrl}}/api/resources/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.5 Http de de ResourceRole
src/http/authorization/resourceRole.http
### resourceRole.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles de recursos
GET {{baseUrl}}/api/resource-roles
### Obtener rol de recurso por ID
GET {{baseUrl}}/api/resource-roles/1
### Crear un nuevo rol de recurso
POST {{baseUrl}}/api/resource-roles
Content-Type: application/json
{
"resource_id": 1,
"role_id": 1,
"is_active": "ACTIVE"
}
### Actualizar un rol de recurso
PATCH {{baseUrl}}/api/resource-roles/1
Content-Type: application/json
{
"resource_id": 1,
"role_id": 2,
"is_active": "ACTIVE"
}
### Eliminar un rol de recurso físicamente
DELETE {{baseUrl}}/api/resource-roles/1
### Eliminar un rol de recurso lógicamente
DELETE {{baseUrl}}/api/resource-roles/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.6 Http de de RefreshToken
src/http/authorization/refreshToken.http
### refreshToken.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los refresh tokens
GET {{baseUrl}}/refresk-token
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2 Http de los modelos de Negocio
2.4.2.1 Http de de Client
src/http/client.http
// client.http
import dotenv from "dotenv";
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OCwiaWF0IjoxNzU5OTQyNjMxLCJleHAiOjE3NTk5NDMyMzF9.EAhQ6CjPXiwsq-hbvUSn9EXKF4CXcD-Q9IYMYlcOh3k
@baseUrl = http://localhost:4000
### Peticiones sin TOKEN
### Obtener todos los clientes
GET {{baseUrl}}/api/clientes
### Obtener cliente por ID
GET {{baseUrl}}/api/clientes/1
### Crear un nuevo cliente
POST {{baseUrl}}/api/clientes
Content-Type: application/json
{
"name": "Jaider Quintero",
"address": "Km 1",
"phone": "3001234567",
"email": "jqm@gmail.com",
"password": "12345678",
"status": "ACTIVE"
}
### Actualizar un cliente
PATCH {{baseUrl}}/api/clientes/1
Content-Type: application/json
{
"name": "Juan Perez Actualizado",
"address": "Calle 456",
"phone": "3007654321",
"email": "juan.perez@example.com",
"password": "654321",
"status": "ACTIVE"
}
### Eliminar un cliente físicamente
DELETE {{baseUrl}}/api/clientes/1
### Eliminar un cliente lógicamente
DELETE {{baseUrl}}/api/clientes/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.2 Http de de ProductType
src/http/productType.http
### productType.http
@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los tipos de producto
GET {{baseUrl}}/product-types
### Obtener tipo de producto por ID
GET {{baseUrl}}/product-types/1
### Crear un nuevo tipo de producto
POST {{baseUrl}}/product-types
Content-Type: application/json
{
"name": "Electrónicos",
"description": "Productos electrónicos y tecnológicos",
"status": "ACTIVE"
}
### Actualizar un tipo de producto
PATCH {{baseUrl}}/product-types/1
Content-Type: application/json
{
"name": "Electrónicos Actualizados",
"description": "Productos electrónicos y tecnológicos actualizados",
"status": "ACTIVE"
}
### Eliminar un tipo de producto físicamente
DELETE {{baseUrl}}/product-types/1
### Eliminar un tipo de producto lógicamente
DELETE {{baseUrl}}/product-types/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.3 Http de de Product
src/http/product.http
### product.http
@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los productos
GET {{baseUrl}}/products
### Obtener producto por ID
GET {{baseUrl}}/products/1
### Crear un nuevo producto
POST {{baseUrl}}/products
Content-Type: application/json
{
"name": "Laptop HP",
"brand": "HP",
"price": 1500000,
"min_stock": 5,
"quantity": 10,
"product_type_id": 1,
"status": "ACTIVE"
}
### Actualizar un producto
PATCH {{baseUrl}}/products/1
Content-Type: application/json
{
"name": "Laptop HP Actualizada",
"brand": "HP",
"price": 1600000,
"min_stock": 3,
"quantity": 8,
"product_type_id": 1,
"status": "ACTIVE"
}
### Eliminar un producto físicamente
DELETE {{baseUrl}}/products/1
### Eliminar un producto lógicamente
DELETE {{baseUrl}}/products/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.4 Http de de Sale
src/http/sale.http
### sale.http
@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todas las ventas
GET {{baseUrl}}/ventas
### Obtener venta por ID
GET {{baseUrl}}/ventas/1
### Crear una nueva venta
POST {{baseUrl}}/ventas
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 100000,
"tax": 19000,
"discounts": 5000,
"total": 114000,
"status": "ACTIVE",
"client_id": 1
}
### Actualizar una venta
PATCH {{baseUrl}}/ventas/1
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 120000,
"tax": 22800,
"discounts": 10000,
"total": 132800,
"status": "ACTIVE",
"client_id": 1
}
### Eliminar una venta físicamente
DELETE {{baseUrl}}/ventas/1
### Eliminar una venta lógicamente
DELETE {{baseUrl}}/ventas/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.5 Http de de ProductSale
src/http/productSale.http
### productSale.http
@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todas las ventas de productos
GET {{baseUrl}}/product-sales
### Obtener venta de producto por ID
GET {{baseUrl}}/product-sales/1
### Crear una nueva venta de producto
POST {{baseUrl}}/product-sales
Content-Type: application/json
{
"total": 1500000,
"sale_id": 1,
"product_id": 1
}
### Actualizar una venta de producto
PATCH {{baseUrl}}/product-sales/1
Content-Type: application/json
{
"total": 1600000,
"sale_id": 1,
"product_id": 1
}
### Eliminar una venta de producto físicamente
DELETE {{baseUrl}}/product-sales/1
### Eliminar una venta de producto lógicamente
DELETE {{baseUrl}}/product-sales/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
2.4 Como Probar las Peticiones API REST
Tomaremos como ejemplo las Rutas del Modelo Client
Para ello visualizamos dos archivos
-
src/http/client.http -
src/routes/client.ts
En el archivo client.http se encuentran todas las rutas definidas en src/routes/client.ts
Se procede asi:

Y Asi se ve el resultado

Paso 3: Datos de Prueba (Opcional)
3.1 Instalar faker para datos de prueba
3.2 Crear script de población de datos
import { Client } from '../models/Client';
import { ProductType } from '../models/ProductType';
import { Product } from '../models/Product';
import { Sale } from '../models/Sale';
import { ProductSale } from '../models/ProductSale';
import { faker } from '@faker-js/faker';
async function createFakeData() {
// Crear clientes falsos
for (let i = 0; i < 50; i++) {
await Client.create({
name: faker.person.fullName(),
address: faker.location.streetAddress(),
phone: faker.phone.number(), // Genera un número de teléfono aleatorio
email: faker.internet.email(),
password: faker.internet.password(),
status: 'ACTIVE',
});
}
// Crear tipos de productos falsos
for (let i = 0; i < 10; i++) {
await ProductType.create({
name: faker.commerce.department(),
description: faker.commerce.productDescription(),
status: 'ACTIVE',
});
}
// Crear productos falsos
const typeProducts = await ProductType.findAll();
for (let i = 0; i < 20; i++) {
await Product.create({
name: faker.commerce.productName(),
brand: faker.company.name(),
price: faker.number.bigInt(),
min_stock: faker.number.int({ min: 1, max: 10 }),
quantity: faker.number.int({ min: 1, max: 100 }),
product_type_id: typeProducts.length > 0
? typeProducts[faker.number.int({ min: 0, max: typeProducts.length - 1 })]?.id
: null,
status: 'ACTIVE',
});
}
// Crear ventas falsas
const clients = await Client.findAll();
for (let i = 0; i < 100; i++) {
await Sale.create({
sale_date: faker.date.past(),
subtotal: faker.number.bigInt(),
tax: faker.number.bigInt(),
discounts: faker.number.bigInt(),
total: faker.number.bigInt(),
status: 'ACTIVE',
client_id: clients.length > 0
? clients[faker.number.int({ min: 0, max: clients.length - 1 })]?.id ?? null
: null
});
}
// // Crear productos ventas falsos
const sales = await Sale.findAll();
const products = await Product.findAll();
for (let i = 0; i < 200; i++) {
await ProductSale.create({
total: faker.number.bigInt(),
sale_id: sales[faker.number.int({ min: 0, max: sales.length - 1 })]?.id ?? null,
product_id: products[faker.number.int({ min: 0, max: products.length - 1 })]?.id ?? null
});
}
}
createFakeData().then(() => {
console.log('Datos falsos creados exitosamente');
}).catch((error) => {
console.error('Error al crear datos falsos:', error);
});
// Para ejecutar este script, ejecute el siguiente comando:
// npm install -g ts-node
// ts-node src/faker/populate_data.ts
// npm install @faker-js/faker
Paso 4: Gitignore
1.1 Crear .gitignore
Paso 5: Ejecución del Proyecto
5.1 Scripts de package.json
{
"scripts": {
"build": "tsc",
"dev": "nodemon src/server.ts --exec ts-node",
"start": "node dist/server.js",
"populate": "ts-node src/faker/populate_data.ts"
}
}
5.2 Ejecutar el proyecto
npm run dev # Desarrollo
npm run build # Producción
npm start
npm run populate # Poblar datos de prueba
Finalmente,

Paso 6: Implementacion de Autenticacion y Autorizacion
6.1 Controlador Auth
Incluimos en los cotroladores un nuevo archivo para registrar usuarios y realizar login
src/controllers/Authorization/auth.controller.ts
import { Request, Response } from 'express';
import { User } from '../../models/authorization/User';
export class AuthController {
public async register(req: Request, res: Response): Promise<void> {
try {
const { username, email, password, is_active, avatar } = req.body;
const user_interface: User = await User.create({ username, email, password, is_active, avatar });
const token = user_interface.generateToken();
res.status(201).json({ user_interface, token });
} catch (error) {
res.status(500).json({ error: 'Error al registrar el usuario' });
}
}
public async login(req: Request, res: Response): Promise<void> {
try {
const { email, password } = req.body;
const user: User | null = await User.findOne(
{
where: {
email,
is_active: true
}
});
if (!user || !(await user.checkPassword(password))) {
res.status(401).json({ error: 'Credenciales inválidas' });
return;
}
const token = user.generateToken();
res.status(200).json({ user, token });
} catch (error) {
res.status(500).json({ error: 'Error al iniciar sesión' });
}
}
}
6.2 Ruta de Auth
src/routes/Authorization/auth.ts
import { Application } from "express";
import { AuthController } from '../../controllers/authorization/auth.controller';
export class AuthRoutes {
public authController: AuthController = new AuthController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/register")
.post(this.authController.register);
app.route("/api/login")
.post(this.authController.login);
// ================== RUTAS CON AUTENTICACIÓN ==================
}
}
Agregar estas rutas en
src/routes/Authorization/index.ts
.........
.........
.........
import { AuthRoutes } from "./authorization/auth"; // Add this import
export class Routes {
......
......
public authRoutes: AuthRoutes = new AuthRoutes(); // Add this line
}
debe quedar asi:
import { Router } from "express";
import { ClientRoutes } from "./client";
import { SaleRoutes } from "./sale";
import { ProductRoutes } from "./product";
import { ProductTypeRoutes } from "./productType";
import { ProductSaleRoutes } from "./productSale";
import { UserRoutes } from "./authorization/user";
import { RoleRoutes } from "./authorization/role";
import { RoleUserRoutes } from "./authorization/role_user";
import { RefreshTokenRoutes } from "./authorization/refresk_token";
import { ResourceRoutes } from "./authorization/resource"; // Import ResourceRoutes
import { ResourceRoleRoutes } from "./authorization/resourceRole"; // Import ResourceRoleRoutes
import { AuthRoutes } from "./authorization/auth"; // Add this import
export class Routes {
public clientRoutes: ClientRoutes = new ClientRoutes();
public saleRoutes: SaleRoutes = new SaleRoutes();
public productRoutes: ProductRoutes = new ProductRoutes();
public productTypeRoutes: ProductTypeRoutes = new ProductTypeRoutes();
public productSaleRoutes: ProductSaleRoutes = new ProductSaleRoutes();
public userRoutes: UserRoutes = new UserRoutes();
public roleRoutes: RoleRoutes = new RoleRoutes();
public roleUserRoutes: RoleUserRoutes = new RoleUserRoutes();
public refreshTokenRoutes: RefreshTokenRoutes = new RefreshTokenRoutes();
public resourceRoutes: ResourceRoutes = new ResourceRoutes(); // Add ResourceRoutes
public resourceRoleRoutes: ResourceRoleRoutes = new ResourceRoleRoutes(); // Add ResourceRoutes
public authRoutes: AuthRoutes = new AuthRoutes(); // Add this line
}
y cerrar el ciclo actulizando las rutas en
src/config/index.http
// ...existing code...
// Route configuration
.....
.....
.....
private routes(): void {
.....
....
...
....
this.routePrv.authRoutes.routes(this.app)
}
Codigo index completo
import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize, testConnection, getDatabaseInfo } from "../database/db";
import { Routes } from "../routes/index";
var cors = require("cors");
dotenv.config();
export class App {
public app: Application;
public routePrv: Routes = new Routes();
constructor(private port?: number | string) {
this.app = express();
this.settings();
this.middlewares();
this.routes();
this.dbConnection();
}
private settings(): void {
this.app.set('port', this.port || process.env.PORT || 4000);
}
private middlewares(): void {
this.app.use(morgan('dev'));
this.app.use(cors());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
}
// Route configuration
private routes(): void {
this.routePrv.clientRoutes.routes(this.app);
this.routePrv.saleRoutes.routes(this.app);
this.routePrv.productRoutes.routes(this.app);
this.routePrv.productTypeRoutes.routes(this.app);
this.routePrv.productSaleRoutes.routes(this.app);
this.routePrv.userRoutes.routes(this.app);
this.routePrv.roleRoutes.routes(this.app);
this.routePrv.roleUserRoutes.routes(this.app);
this.routePrv.refreshTokenRoutes.routes(this.app);
this.routePrv.resourceRoutes.routes(this.app);
this.routePrv.resourceRoleRoutes.routes(this.app);
this.routePrv.authRoutes.routes(this.app)
}
private async dbConnection(): Promise<void> {
try {
// Mostrar información de la base de datos seleccionada
const dbInfo = getDatabaseInfo();
console.log(`🔗 Intentando conectar a: ${dbInfo.engine.toUpperCase()}`);
// Probar la conexión
const isConnected = await testConnection();
if (!isConnected) {
throw new Error(`No se pudo conectar a la base de datos ${dbInfo.engine.toUpperCase()}`);
}
// Sincronizar la base de datos
await sequelize.sync({ force: false });
console.log(`📦 Base de datos sincronizada exitosamente`);
} catch (error) {
console.error("❌ Error al conectar con la base de datos:", error);
process.exit(1); // Terminar la aplicación si no se puede conectar
}
}
async listen() {
await this.app.listen(this.app.get('port'));
console.log(`🚀 Servidor ejecutándose en puerto ${this.app.get('port')}`);
}
}
6.3 Http Auth
src/http/Authorization/auth.http
### auth.http
@baseUrl = http://localhost:4000
# @token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Registrar un nuevo usuario
POST {{baseUrl}}/api/register
Content-Type: application/json
{
"username": "jquintero",
"email": "jq@gmail.com",
"password": "12345678",
"is_active": "ACTIVE",
"avatar": "avatar.jpg"
}
### Iniciar sesión
POST {{baseUrl}}/api/login
Content-Type: application/json
{
"email": "jq@gmail.com",
"password": "12345678"
}
### ================== RUTAS CON AUTENTICACIÓN ==================
Probar Registrar Usuario

Probar Registrar Login

6.4 Configurar Middleware
Con el Middleware protegemos todas las rutas usando JWT TOKEN
src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { RefreshToken } from '../models/authorization/RefreshToken';
import { User } from '../models/authorization/User';
export const authMiddleware = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const token = req.header('Authorization')?.replace('Bearer ', '');
const currentRoute = req.originalUrl;
const currentMethod = req.method;
console.log(currentRoute)
if (!token) {
res.status(401).json({ error: 'Acceso denegado: No se proporcionó el token principal.' });
return;
}
try {
// Verificar si el token principal es válido
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret') as jwt.JwtPayload;
// Buscar el usuario en la base de datos
const user: User | null = await User.findOne({ where: { id: decoded.id, is_active: 'ACTIVE' } });
if (!user) {
res.status(401).json({ error: 'Usuario no encontrado o inactivo.' });
return;
}
// Continuar con la solicitud
next();
} catch (error: any) {
if (error.name === 'TokenExpiredError') {
res.status(401).json({ error: 'El token principal ha expirado.' });
} else if (error.name === 'JsonWebTokenError') {
res.status(401).json({ error: 'Token inválido.' });
} else {
res.status(500).json({ error: 'Error interno del servidor.', details: error.message });
}
}
};
Y para probar protegemos una ruta con token por ejemplo en cliente
src/routes/client.ts
import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";
import { authMiddleware } from "../middleware/auth";
export class ClientRoutes {
public clientController: ClientController = new ClientController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/clientes/public")
.get(this.clientController.getAllClients)
.post(this.clientController.createClient);
app.route("/api/clientes/public/:id")
.get(this.clientController.getClientById)
.patch(this.clientController.updateClient)
.delete(this.clientController.deleteClient);
app.route("/api/clientes/public/:id/logic")
.delete(this.clientController.deleteClientAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/clientes")
.get(authMiddleware, this.clientController.getAllClients)
}
}
Notese que cambie el end point de las rutas sin token las que tienen token para que no sean iguales y pueda realizar bien la proteccion de la ruta
Primero debemos registar el usuario para obtener el token o en su defecto solo hacer el login si ya lo hemos registrado.
Luego en el http de client.http pegar el token obtenido y ejecutar la ruta protegida de mostrar todos los clientes
src/http/client.http
// client.http
import dotenv from "dotenv";
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
@baseUrl = http://localhost:4000
### Peticiones sin TOKEN
### Obtener todos los clientes
GET {{baseUrl}}/api/clientes/public
### Obtener cliente por ID
GET {{baseUrl}}/api/clientes/public/1
### Crear un nuevo cliente
POST {{baseUrl}}/api/clientes/public
Content-Type: application/json
{
"name": "Jaider Quintero",
"address": "Km 1",
"phone": "3001234567",
"email": "jqm@gmail.com",
"password": "12345678",
"status": "ACTIVE"
}
### Actualizar un cliente
PATCH {{baseUrl}}/api/clientes/public/1
Content-Type: application/json
{
"name": "Juan Perez Actualizado",
"address": "Calle 456",
"phone": "3007654321",
"email": "juan.perez@example.com",
"password": "654321",
"status": "ACTIVE"
}
### Eliminar un cliente físicamente
DELETE {{baseUrl}}/api/clientes/public/1
### Eliminar un cliente lógicamente
DELETE {{baseUrl}}/api/clientes/public/1/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los clientes (CON token)
GET {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}
Hacemos lo mismo para el resto de las rutas de clientes e iguamente para el resto de las rutas de los modelos
6.5 Autenticacion con Token Rutas Modelos Autorizacion
6.5.1 Ruta y Http User
No haremos cambio en User dado que lo mabejaremos con Auth
6.5.2 Ruta y Http Role
src/routes/authorization/role.ts
import { Application } from "express";
import { RoleController } from '../../controllers/authorization/role.controller';
import { authMiddleware } from '../../middleware/auth';
export class RoleRoutes {
public roleController: RoleController = new RoleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/roles/public")
.get(this.roleController.getAllRoles)
.post(this.roleController.createRole);
app.route("/api/roles/public/:id")
.get(this.roleController.getRoleById)
.patch(this.roleController.updateRole)
.delete(this.roleController.deleteRole);
app.route("/api/roles/public/:id/logic")
.delete(this.roleController.deleteRoleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/roles")
.get(authMiddleware, this.roleController.getAllRoles)
.post(authMiddleware, this.roleController.createRole);
app.route("/api/roles/:id")
.get(authMiddleware, this.roleController.getRoleById)
.patch(authMiddleware, this.roleController.updateRole)
.delete(authMiddleware, this.roleController.deleteRole);
app.route("/api/roles/:id/logic")
.delete(authMiddleware, this.roleController.deleteRoleAdv);
}
}
src/http/authorization/role.ts
### role.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles (SIN token)
GET {{baseUrl}}/api/roles/public
### Obtener rol por ID (SIN token)
GET {{baseUrl}}/api/roles/public/1
### Crear un nuevo rol (SIN token)
POST {{baseUrl}}/api/roles/public
Content-Type: application/json
{
"name": "Administrator",
"is_active": "ACTIVE"
}
### Actualizar un rol (SIN token)
PATCH {{baseUrl}}/api/roles/public/1
Content-Type: application/json
{
"name": "Super Administrator",
"is_active": "ACTIVE"
}
### Eliminar un rol físicamente (SIN token)
DELETE {{baseUrl}}/api/roles/public/1
### Eliminar un rol lógicamente (SIN token)
DELETE {{baseUrl}}/api/roles/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los roles (CON token)
GET {{baseUrl}}/api/roles
Authorization: Bearer {{token}}
### Obtener rol por ID (CON token)
GET {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}
### Crear un nuevo rol (CON token)
POST {{baseUrl}}/api/roles
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Manager",
"is_active": "ACTIVE"
}
### Actualizar un rol (CON token)
PATCH {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Senior Manager",
"is_active": "ACTIVE"
}
### Eliminar un rol físicamente (CON token)
DELETE {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}
### Eliminar un rol lógicamente (CON token)
DELETE {{baseUrl}}/api/roles/1/logic
Authorization: Bearer {{token}}
6.5.3 Ruta y Http RoleUser
src/routes/authorization/role_user.ts
import { Application } from "express";
import { RoleUserController } from '../../controllers/authorization/role_user.controller';
import { authMiddleware } from '../../middleware/auth';
export class RoleUserRoutes {
public roleUserController: RoleUserController = new RoleUserController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/roleUsers/public")
.get(this.roleUserController.getAllRoleUsers)
.post(this.roleUserController.createRoleUser);
app.route("/api/roleUsers/public/:id")
.get(this.roleUserController.getRoleUserById)
.patch(this.roleUserController.updateRoleUser)
.delete(this.roleUserController.deleteRoleUser);
app.route("/api/roleUsers/public/:id/logic")
.delete(this.roleUserController.deleteRoleUserAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/roleUsers")
.get(authMiddleware, this.roleUserController.getAllRoleUsers)
.post(authMiddleware, this.roleUserController.createRoleUser);
app.route("/api/roleUsers/:id")
.get(authMiddleware, this.roleUserController.getRoleUserById)
.patch(authMiddleware, this.roleUserController.updateRoleUser)
.delete(authMiddleware, this.roleUserController.deleteRoleUser);
app.route("/api/roleUsers/:id/logic")
.delete(authMiddleware, this.roleUserController.deleteRoleUserAdv);
}
}
src/http/authorization/role_user.ts
### roleUser.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles de usuario (SIN token)
GET {{baseUrl}}/api/roleUsers/public
### Obtener rol de usuario por ID (SIN token)
GET {{baseUrl}}/api/roleUsers/public/1
### Crear un nuevo rol de usuario (SIN token)
POST {{baseUrl}}/api/roleUsers/public
Content-Type: application/json
{
"role_id": 1,
"user_id": 1,
"is_active": "ACTIVE"
}
### Actualizar un rol de usuario (SIN token)
PATCH {{baseUrl}}/api/roleUsers/public/1
Content-Type: application/json
{
"role_id": 2,
"user_id": 1,
"is_active": "ACTIVE"
}
### Eliminar un rol de usuario físicamente (SIN token)
DELETE {{baseUrl}}/api/roleUsers/public/1
### Eliminar un rol de usuario lógicamente (SIN token)
DELETE {{baseUrl}}/api/roleUsers/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los roles de usuario (CON token)
GET {{baseUrl}}/api/roleUsers
Authorization: Bearer {{token}}
### Obtener rol de usuario por ID (CON token)
GET {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}
### Crear un nuevo rol de usuario (CON token)
POST {{baseUrl}}/api/roleUsers
Authorization: Bearer {{token}}
Content-Type: application/json
{
"role_id": 1,
"user_id": 2,
"is_active": "ACTIVE"
}
### Actualizar un rol de usuario (CON token)
PATCH {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"role_id": 3,
"user_id": 2,
"is_active": "ACTIVE"
}
### Eliminar un rol de usuario físicamente (CON token)
DELETE {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}
### Eliminar un rol de usuario lógicamente (CON token)
DELETE {{baseUrl}}/api/roleUsers/1/logic
Authorization: Bearer {{token}}
6.5.4 Ruta y Http Resource
src/routes/authorization/resource.ts
import { Application } from "express";
import { ResourceController } from "../../controllers/authorization/resource.controller";
import { authMiddleware } from '../../middleware/auth';
export class ResourceRoutes {
public resourceController: ResourceController = new ResourceController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/resources/public")
.get(this.resourceController.getAllResources)
.post(this.resourceController.createResource);
app.route("/api/resources/public/:id")
.get(this.resourceController.getResourceById)
.patch(this.resourceController.updateResource)
.delete(this.resourceController.deleteResource);
app.route("/api/resources/public/:id/logic")
.delete(this.resourceController.deleteResourceAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/resources")
.get(authMiddleware, this.resourceController.getAllResources)
.post(authMiddleware, this.resourceController.createResource);
app.route("/api/resources/:id")
.get(authMiddleware, this.resourceController.getResourceById)
.patch(authMiddleware, this.resourceController.updateResource)
.delete(authMiddleware, this.resourceController.deleteResource);
app.route("/api/resources/:id/logic")
.delete(authMiddleware, this.resourceController.deleteResourceAdv);
}
}
src/http/authorization/resource.ts
### resource.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los recursos (SIN token)
GET {{baseUrl}}/api/resources/public
### Obtener recurso por ID (SIN token)
GET {{baseUrl}}/api/resources/public/1
### Crear un nuevo recurso (SIN token)
POST {{baseUrl}}/api/resources/public
Content-Type: application/json
{
"path": "/api/clientes",
"method": "GET",
"is_active": "ACTIVE"
}
### Actualizar un recurso (SIN token)
PATCH {{baseUrl}}/api/resources/public/1
Content-Type: application/json
{
"path": "/api/clientes/:id",
"method": "GET",
"is_active": "ACTIVE"
}
### Eliminar un recurso físicamente (SIN token)
DELETE {{baseUrl}}/api/resources/public/1
### Eliminar un recurso lógicamente (SIN token)
DELETE {{baseUrl}}/api/resources/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los recursos (CON token)
GET {{baseUrl}}/api/resources
Authorization: Bearer {{token}}
### Obtener recurso por ID (CON token)
GET {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}
### Crear un nuevo recurso (CON token)
POST {{baseUrl}}/api/resources
Authorization: Bearer {{token}}
Content-Type: application/json
{
"path": "/api/users",
"method": "POST",
"is_active": "ACTIVE"
}
### Actualizar un recurso (CON token)
PATCH {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"path": "/api/users/:id",
"method": "PATCH",
"is_active": "ACTIVE"
}
### Eliminar un recurso físicamente (CON token)
DELETE {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}
### Eliminar un recurso lógicamente (CON token)
DELETE {{baseUrl}}/api/resources/1/logic
Authorization: Bearer {{token}}
6.5.5 Ruta y Http ResourceRole
src/routes/authorization/resourceRole.ts
import { Application } from "express";
import { ResourceRoleController } from "../../controllers/authorization/resourceRole.controller";
import { authMiddleware } from '../../middleware/auth';
export class ResourceRoleRoutes {
public resourceRoleController: ResourceRoleController = new ResourceRoleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/resourceRoles/public")
.get(this.resourceRoleController.getAllResourceRoles)
.post(this.resourceRoleController.createResourceRole);
app.route("/api/resourceRoles/public/:id")
.get(this.resourceRoleController.getResourceRoleById)
.patch(this.resourceRoleController.updateResourceRole)
.delete(this.resourceRoleController.deleteResourceRole);
app.route("/api/resourceRoles/public/:id/logic")
.delete(this.resourceRoleController.deleteResourceRoleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/resourceRoles")
.get(authMiddleware, this.resourceRoleController.getAllResourceRoles)
.post(authMiddleware, this.resourceRoleController.createResourceRole);
app.route("/api/resourceRoles/:id")
.get(authMiddleware, this.resourceRoleController.getResourceRoleById)
.patch(authMiddleware, this.resourceRoleController.updateResourceRole)
.delete(authMiddleware, this.resourceRoleController.deleteResourceRole);
app.route("/api/resourceRoles/:id/logic")
.delete(authMiddleware, this.resourceRoleController.deleteResourceRoleAdv);
}
}
src/http/authorization/resourceRole.ts
### resourceRole.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los roles de recursos (SIN token)
GET {{baseUrl}}/api/resourceRoles/public
### Obtener rol de recurso por ID (SIN token)
GET {{baseUrl}}/api/resourceRoles/public/1
### Crear un nuevo rol de recurso (SIN token)
POST {{baseUrl}}/api/resourceRoles/public
Content-Type: application/json
{
"resource_id": 1,
"role_id": 1,
"is_active": "ACTIVE"
}
### Actualizar un rol de recurso (SIN token)
PATCH {{baseUrl}}/api/resourceRoles/public/1
Content-Type: application/json
{
"resource_id": 1,
"role_id": 2,
"is_active": "ACTIVE"
}
### Eliminar un rol de recurso físicamente (SIN token)
DELETE {{baseUrl}}/api/resourceRoles/public/1
### Eliminar un rol de recurso lógicamente (SIN token)
DELETE {{baseUrl}}/api/resourceRoles/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los roles de recursos (CON token)
GET {{baseUrl}}/api/resourceRoles
Authorization: Bearer {{token}}
### Obtener rol de recurso por ID (CON token)
GET {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}
### Crear un nuevo rol de recurso (CON token)
POST {{baseUrl}}/api/resourceRoles
Authorization: Bearer {{token}}
Content-Type: application/json
{
"resource_id": 2,
"role_id": 1,
"is_active": "ACTIVE"
}
### Actualizar un rol de recurso (CON token)
PATCH {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"resource_id": 2,
"role_id": 3,
"is_active": "ACTIVE"
}
### Eliminar un rol de recurso físicamente (CON token)
DELETE {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}
### Eliminar un rol de recurso lógicamente (CON token)
DELETE {{baseUrl}}/api/resourceRoles/1/logic
Authorization: Bearer {{token}}
6.5.5 Ruta y Http RefreshToken
No trabajaremos la parte de autenticacion y autorizacion este modelo
6.5 Autenticacion con Token Rutas Modelos del Negocio
6.5.1 Ruta y Http Client
src/routes/client.ts
import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";
import { authMiddleware } from "../middleware/auth";
export class ClientRoutes {
public clientController: ClientController = new ClientController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/clientes/public")
.get(this.clientController.getAllClients)
.post(this.clientController.createClient);
app.route("/api/clientes/public/:id")
.get(this.clientController.getClientById)
.patch(this.clientController.updateClient)
.delete(this.clientController.deleteClient);
app.route("/api/clientes/public/:id/logic")
.delete(this.clientController.deleteClientAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/clientes")
.get(authMiddleware, this.clientController.getAllClients)
.post(authMiddleware, this.clientController.createClient);
app.route("/api/clientes/:id")
.get(authMiddleware, this.clientController.getClientById)
.patch(authMiddleware, this.clientController.updateClient)
.delete(authMiddleware, this.clientController.deleteClient);
app.route("/api/clientes/:id/logic")
.delete(authMiddleware, this.clientController.deleteClientAdv);
}
}
src/http/client.http
// client.http
import dotenv from "dotenv";
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
@baseUrl = http://localhost:4000
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los clientes (SIN token)
GET {{baseUrl}}/api/clientes/public
### Obtener cliente por ID (SIN token)
GET {{baseUrl}}/api/clientes/public/1
### Crear un nuevo cliente (SIN token)
POST {{baseUrl}}/api/clientes/public
Content-Type: application/json
{
"name": "Sandy Romero",
"address": "Km 1",
"phone": "3001234567",
"email": "srm@gmail.com",
"password": "12345678",
"status": "ACTIVE"
}
### Actualizar un cliente (SIN token)
PATCH {{baseUrl}}/api/clientes/public/1
Content-Type: application/json
{
"name": "Juan Perez Actualizado",
"address": "Calle 456",
"phone": "3007654321",
"email": "juan.perez@example.com",
"password": "654321",
"status": "ACTIVE"
}
### Eliminar un cliente físicamente (SIN token)
DELETE {{baseUrl}}/api/clientes/public/1
### Eliminar un cliente lógicamente (SIN token)
DELETE {{baseUrl}}/api/clientes/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los clientes (CON token)
GET {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}
### Obtener cliente por ID (CON token)
GET {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}
### Crear un nuevo cliente (CON token)
POST {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "María García",
"address": "Calle 123",
"phone": "3009876543",
"email": "maria.garcia@example.com",
"password": "87654321",
"status": "ACTIVE"
}
### Actualizar un cliente (CON token)
PATCH {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "María García Actualizada",
"address": "Avenida 456",
"phone": "3001234567",
"email": "maria.garcia.updated@example.com",
"password": "newpassword123",
"status": "ACTIVE"
}
### Eliminar un cliente físicamente (CON token)
DELETE {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}
### Eliminar un cliente lógicamente (CON token)
DELETE {{baseUrl}}/api/clientes/1/logic
Authorization: Bearer {{token}}
6.5.2 Ruta y Http ProductType
src/routes/productType.ts
import { Application } from "express";
import { ProductTypeController } from "../controllers/productType.controller";
import { authMiddleware } from "../middleware/auth";
export class ProductTypeRoutes {
public productTypeController: ProductTypeController = new ProductTypeController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/product-types/public")
.get(this.productTypeController.getAllProductTypes)
.post(this.productTypeController.createProductType);
app.route("/api/product-types/public/:id")
.get(this.productTypeController.getProductTypeById)
.patch(this.productTypeController.updateProductType)
.delete(this.productTypeController.deleteProductType);
app.route("/api/product-types/public/:id/logic")
.delete(this.productTypeController.deleteProductTypeAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/product-types")
.get(authMiddleware, this.productTypeController.getAllProductTypes)
.post(authMiddleware, this.productTypeController.createProductType);
app.route("/api/product-types/:id")
.get(authMiddleware, this.productTypeController.getProductTypeById)
.patch(authMiddleware, this.productTypeController.updateProductType)
.delete(authMiddleware, this.productTypeController.deleteProductType);
app.route("/api/product-types/:id/logic")
.delete(authMiddleware, this.productTypeController.deleteProductTypeAdv);
}
}
src/http/productType.http
### productType.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los tipos de producto (SIN token)
GET {{baseUrl}}/api/product-types/public
### Obtener tipo de producto por ID (SIN token)
GET {{baseUrl}}/api/product-types/public/1
### Crear un nuevo tipo de producto (SIN token)
POST {{baseUrl}}/api/product-types/public
Content-Type: application/json
{
"name": "Electrónicos",
"description": "Productos electrónicos y tecnológicos",
"status": "ACTIVE"
}
### Actualizar un tipo de producto (SIN token)
PATCH {{baseUrl}}/api/product-types/public/1
Content-Type: application/json
{
"name": "Electrónicos Actualizados",
"description": "Productos electrónicos y tecnológicos actualizados",
"status": "ACTIVE"
}
### Eliminar un tipo de producto físicamente (SIN token)
DELETE {{baseUrl}}/api/product-types/public/1
### Eliminar un tipo de producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/product-types/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los tipos de producto (CON token)
GET {{baseUrl}}/api/product-types
Authorization: Bearer {{token}}
### Obtener tipo de producto por ID (CON token)
GET {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}
### Crear un nuevo tipo de producto (CON token)
POST {{baseUrl}}/api/product-types
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Ropa y Accesorios",
"description": "Prendas de vestir y accesorios de moda",
"status": "ACTIVE"
}
### Actualizar un tipo de producto (CON token)
PATCH {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Ropa y Accesorios Premium",
"description": "Prendas de vestir y accesorios de moda premium",
"status": "ACTIVE"
}
### Eliminar un tipo de producto físicamente (CON token)
DELETE {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}
### Eliminar un tipo de producto lógicamente (CON token)
DELETE {{baseUrl}}/api/product-types/1/logic
Authorization: Bearer {{token}}
6.5.3 Ruta y Http Product
src/routes/product.ts
import { Application } from "express";
import { ProductController } from "../controllers/product.controller";
import { authMiddleware } from "../middleware/auth";
export class ProductRoutes {
public productController: ProductController = new ProductController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/products/public")
.get(this.productController.getAllProducts)
.post(this.productController.createProduct);
app.route("/api/products/public/:id")
.get(this.productController.getProductById)
.patch(this.productController.updateProduct)
.delete(this.productController.deleteProduct);
app.route("/api/products/public/:id/logic")
.delete(this.productController.deleteProductAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/products")
.get(authMiddleware, this.productController.getAllProducts)
.post(authMiddleware, this.productController.createProduct);
app.route("/api/products/:id")
.get(authMiddleware, this.productController.getProductById)
.patch(authMiddleware, this.productController.updateProduct)
.delete(authMiddleware, this.productController.deleteProduct);
app.route("/api/products/:id/logic")
.delete(authMiddleware, this.productController.deleteProductAdv);
}
}
src/http/product.http
### product.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todos los productos (SIN token)
GET {{baseUrl}}/api/products/public
### Obtener producto por ID (SIN token)
GET {{baseUrl}}/api/products/public/1
### Crear un nuevo producto (SIN token)
POST {{baseUrl}}/api/products/public
Content-Type: application/json
{
"name": "Laptop HP",
"brand": "HP",
"price": 1500000,
"min_stock": 5,
"quantity": 10,
"product_type_id": 1,
"status": "ACTIVE"
}
### Actualizar un producto (SIN token)
PATCH {{baseUrl}}/api/products/public/1
Content-Type: application/json
{
"name": "Laptop HP Actualizada",
"brand": "HP",
"price": 1600000,
"min_stock": 3,
"quantity": 8,
"product_type_id": 1,
"status": "ACTIVE"
}
### Eliminar un producto físicamente (SIN token)
DELETE {{baseUrl}}/api/products/public/1
### Eliminar un producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/products/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todos los productos (CON token)
GET {{baseUrl}}/api/products
Authorization: Bearer {{token}}
### Obtener producto por ID (CON token)
GET {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}
### Crear un nuevo producto (CON token)
POST {{baseUrl}}/api/products
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "MacBook Air M2",
"brand": "Apple",
"price": 3500000,
"min_stock": 2,
"quantity": 5,
"product_type_id": 1,
"status": "ACTIVE"
}
### Actualizar un producto (CON token)
PATCH {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "MacBook Air M2 Updated",
"brand": "Apple",
"price": 3600000,
"min_stock": 3,
"quantity": 7,
"product_type_id": 1,
"status": "ACTIVE"
}
### Eliminar un producto físicamente (CON token)
DELETE {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}
### Eliminar un producto lógicamente (CON token)
DELETE {{baseUrl}}/api/products/1/logic
Authorization: Bearer {{token}}
6.5.4 Ruta y Http Sale
src/routes/sale.ts
import { Application } from "express";
import { SaleController } from "../controllers/sale.controller";
import { authMiddleware } from "../middleware/auth";
export class SaleRoutes {
public saleController: SaleController = new SaleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/sales/public")
.get(this.saleController.getAllSales)
.post(this.saleController.createSale);
app.route("/api/sales/public/:id")
.get(this.saleController.getSaleById)
.patch(this.saleController.updateSale)
.delete(this.saleController.deleteSale);
app.route("/api/sales/public/:id/logic")
.delete(this.saleController.deleteSaleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/sales")
.get(authMiddleware, this.saleController.getAllSales)
.post(authMiddleware, this.saleController.createSale);
app.route("/api/sales/:id")
.get(authMiddleware, this.saleController.getSaleById)
.patch(authMiddleware, this.saleController.updateSale)
.delete(authMiddleware, this.saleController.deleteSale);
app.route("/api/sales/:id/logic")
.delete(authMiddleware, this.saleController.deleteSaleAdv);
// ================== RUTAS LEGACY (mantener compatibilidad) ==================
app.route("/ventas")
.get(this.saleController.getAllSales)
.post(this.saleController.createSale);
app.route("/ventas/:id")
.get(this.saleController.getSaleById)
.patch(this.saleController.updateSale)
.delete(this.saleController.deleteSale);
app.route("/ventas/:id/logic")
.delete(this.saleController.deleteSaleAdv);
}
}
src/http/sale.http
### sale.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todas las ventas (SIN token)
GET {{baseUrl}}/api/sales/public
### Obtener venta por ID (SIN token)
GET {{baseUrl}}/api/sales/public/1
### Crear una nueva venta (SIN token)
POST {{baseUrl}}/api/sales/public
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 100000,
"tax": 19000,
"discounts": 5000,
"total": 114000,
"status": "ACTIVE",
"client_id": 1
}
### Actualizar una venta (SIN token)
PATCH {{baseUrl}}/api/sales/public/1
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 120000,
"tax": 22800,
"discounts": 10000,
"total": 132800,
"status": "ACTIVE",
"client_id": 1
}
### Eliminar una venta físicamente (SIN token)
DELETE {{baseUrl}}/api/sales/public/1
### Eliminar una venta lógicamente (SIN token)
DELETE {{baseUrl}}/api/sales/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todas las ventas (CON token)
GET {{baseUrl}}/api/sales
Authorization: Bearer {{token}}
### Obtener venta por ID (CON token)
GET {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}
### Crear una nueva venta (CON token)
POST {{baseUrl}}/api/sales
Authorization: Bearer {{token}}
Content-Type: application/json
{
"sale_date": "2024-02-20T14:45:00.000Z",
"subtotal": 250000,
"tax": 47500,
"discounts": 15000,
"total": 282500,
"status": "ACTIVE",
"client_id": 2
}
### Actualizar una venta (CON token)
PATCH {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"sale_date": "2024-02-20T15:00:00.000Z",
"subtotal": 275000,
"tax": 52250,
"discounts": 20000,
"total": 307250,
"status": "ACTIVE",
"client_id": 2
}
### Eliminar una venta físicamente (CON token)
DELETE {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}
### Eliminar una venta lógicamente (CON token)
DELETE {{baseUrl}}/api/sales/1/logic
Authorization: Bearer {{token}}
### ================== RUTAS LEGACY (mantener compatibilidad) ==================
### Obtener todas las ventas (legacy)
GET {{baseUrl}}/ventas
### Obtener venta por ID (legacy)
GET {{baseUrl}}/ventas/1
### Crear una nueva venta (legacy)
POST {{baseUrl}}/ventas
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 100000,
"tax": 19000,
"discounts": 5000,
"total": 114000,
"status": "ACTIVE",
"client_id": 1
}
### Actualizar una venta (legacy)
PATCH {{baseUrl}}/ventas/1
Content-Type: application/json
{
"sale_date": "2024-01-15T10:30:00.000Z",
"subtotal": 120000,
"tax": 22800,
"discounts": 10000,
"total": 132800,
"status": "ACTIVE",
"client_id": 1
}
### Eliminar una venta físicamente (legacy)
DELETE {{baseUrl}}/ventas/1
### Eliminar una venta lógicamente (legacy)
DELETE {{baseUrl}}/ventas/1/logic
6.5.5 Ruta y Http ProductSale
src/routes/productSale.ts
import { Application } from "express";
import { ProductSaleController } from "../controllers/productSale.controller";
import { authMiddleware } from "../middleware/auth";
export class ProductSaleRoutes {
public productSaleController: ProductSaleController = new ProductSaleController();
public routes(app: Application): void {
// ================== RUTAS SIN AUTENTICACIÓN ==================
app.route("/api/product-sales/public")
.get(this.productSaleController.getAllProductSales)
.post(this.productSaleController.createProductSale);
app.route("/api/product-sales/public/:id")
.get(this.productSaleController.getProductSaleById)
.patch(this.productSaleController.updateProductSale)
.delete(this.productSaleController.deleteProductSale);
app.route("/api/product-sales/public/:id/logic")
.delete(this.productSaleController.deleteProductSaleAdv);
// ================== RUTAS CON AUTENTICACIÓN ==================
app.route("/api/product-sales")
.get(authMiddleware, this.productSaleController.getAllProductSales)
.post(authMiddleware, this.productSaleController.createProductSale);
app.route("/api/product-sales/:id")
.get(authMiddleware, this.productSaleController.getProductSaleById)
.patch(authMiddleware, this.productSaleController.updateProductSale)
.delete(authMiddleware, this.productSaleController.deleteProductSale);
app.route("/api/product-sales/:id/logic")
.delete(authMiddleware, this.productSaleController.deleteProductSaleAdv);
// ================== RUTAS LEGACY (mantener compatibilidad) ==================
app.route("/product-sales")
.get(this.productSaleController.getAllProductSales)
.post(this.productSaleController.createProductSale);
app.route("/product-sales/:id")
.get(this.productSaleController.getProductSaleById)
.patch(this.productSaleController.updateProductSale)
.delete(this.productSaleController.deleteProductSale);
app.route("/product-sales/:id/logic")
.delete(this.productSaleController.deleteProductSaleAdv);
}
}
src/http/productSale.http
### productSale.http
@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
### ================== RUTAS SIN AUTENTICACIÓN ==================
### Obtener todas las ventas de productos (SIN token)
GET {{baseUrl}}/api/product-sales/public
### Obtener venta de producto por ID (SIN token)
GET {{baseUrl}}/api/product-sales/public/1
### Crear una nueva venta de producto (SIN token)
POST {{baseUrl}}/api/product-sales/public
Content-Type: application/json
{
"total": 1500000,
"sale_id": 1,
"product_id": 1
}
### Actualizar una venta de producto (SIN token)
PATCH {{baseUrl}}/api/product-sales/public/1
Content-Type: application/json
{
"total": 1600000,
"sale_id": 1,
"product_id": 1
}
### Eliminar una venta de producto físicamente (SIN token)
DELETE {{baseUrl}}/api/product-sales/public/1
### Eliminar una venta de producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/product-sales/public/2/logic
### ================== RUTAS CON AUTENTICACIÓN ==================
### Obtener todas las ventas de productos (CON token)
GET {{baseUrl}}/api/product-sales
Authorization: Bearer {{token}}
### Obtener venta de producto por ID (CON token)
GET {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}
### Crear una nueva venta de producto (CON token)
POST {{baseUrl}}/api/product-sales
Authorization: Bearer {{token}}
Content-Type: application/json
{
"total": 2500000,
"sale_id": 2,
"product_id": 2
}
### Actualizar una venta de producto (CON token)
PATCH {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}
Content-Type: application/json
{
"total": 2700000,
"sale_id": 2,
"product_id": 2
}
### Eliminar una venta de producto físicamente (CON token)
DELETE {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}
### Eliminar una venta de producto lógicamente (CON token)
DELETE {{baseUrl}}/api/product-sales/1/logic
Authorization: Bearer {{token}}
### ================== RUTAS LEGACY (mantener compatibilidad) ==================
### Obtener todas las ventas de productos (legacy)
GET {{baseUrl}}/product-sales
### Obtener venta de producto por ID (legacy)
GET {{baseUrl}}/product-sales/1
### Crear una nueva venta de producto (legacy)
POST {{baseUrl}}/product-sales
Content-Type: application/json
{
"total": 1500000,
"sale_id": 1,
"product_id": 1
}
### Actualizar una venta de producto (legacy)
PATCH {{baseUrl}}/product-sales/1
Content-Type: application/json
{
"total": 1600000,
"sale_id": 1,
"product_id": 1
}
### Eliminar una venta de producto físicamente (legacy)
DELETE {{baseUrl}}/product-sales/1
### Eliminar una venta de producto lógicamente (legacy)
DELETE {{baseUrl}}/product-sales/1/logic
6.5 Implementar la Autorizacion
Para ello debemos insertar los registros a todas las tablas de los modelos de Athorization.
Luego, implementar el auth del middleware de la siguiente manera:
src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { User } from '../models/authorization/User';
import { Role } from '../models/authorization/Role';
import { ResourceRole } from '../models/authorization/ResourceRole';
import { Resource } from '../models/authorization/Resource';
import { RoleUser } from '../models/authorization/RoleUser';
import { pathToRegexp } from 'path-to-regexp'; // Importar path-to-regexp
import { addEmitHelper } from 'typescript';
export const authMiddleware = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const token = req.header('Authorization')?.replace('Bearer ', '');
const currentRoute = req.originalUrl;
const currentMethod = req.method;
if (!token) {
res.status(401).json({ error: 'Acceso denegado: No se proporcionó el token principal.' });
return;
}
try {
// Verificar si el token principal es válido
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret') as jwt.JwtPayload;
// Buscar el usuario en la base de datos
const user: User | null = await User.findOne({ where: { id: decoded.id, is_active: 'ACTIVE' } });
if (!user) {
res.status(401).json({ error: 'Usuario no encontrado o inactivo.' });
return;
}
// Validar autorización
const isAuthorized = await validateAuthorization(decoded.id, currentRoute, currentMethod);
if (!isAuthorized) {
res.status(403).json({ error: 'No está autorizado para ejecutar esta petición.' });
return;
}
// Continuar con la solicitud
next();
} catch (error: any) {
if (error.name === 'TokenExpiredError') {
res.status(401).json({ error: 'El token principal ha expirado.' });
} else if (error.name === 'JsonWebTokenError') {
res.status(401).json({ error: 'Token inválido.' });
} else {
res.status(500).json({ error: 'Error interno del servidor.', details: error.message });
}
}
};
export const validateAuthorization = async (userId: number, resourcePath: string, resourceMethod: string): Promise<boolean> => {
try {
// Obtener todos los recursos activos que coincidan con el método
const resources = await Resource.findAll({
where: { method: resourceMethod, is_active: "ACTIVE" },
});
// Convertir las rutas dinámicas a expresiones regulares y buscar coincidencias
const matchingResource = resources.find((resource) => {
const regex = pathToRegexp(resource.path).regexp.test(resourcePath);
return regex;
});
if (!matchingResource) {
return false; // No hay coincidencias para la ruta y el método
}
// Verificar si existe una relación válida entre el usuario, su rol y el recurso solicitado
const resourceRole = await ResourceRole.findOne({
include: [
{
model: Role,
include: [
{
model: RoleUser,
where: { user_id: userId, is_active: "ACTIVE" }, // Validar que el usuario esté asociado al rol
},
],
where: { is_active: "ACTIVE" }, // Validar que el rol esté activo
},
],
where: { resource_id: matchingResource.id, is_active: "ACTIVE" }, // Validar que la relación resource_role esté activa
});
return !!resourceRole; // Retorna true si se encuentra un registro coincidente
} catch (error) {
console.error('Error al validar la autorización:', error);
return false;
}
};