Creación de Modelos
Los modelos hacen referencia a las tablas del diseño de su base de datos. Para este caso asumimos el siguiente diseño.
Modelos del Core del Negocio

Modelos del Esquema de Autorizacion

Detalle de los campos de las tablas
Tablas de Autorización
1. users
-
id (PK, AUTO_INCREMENT)
-
username (STRING, NOT NULL)
-
email (STRING, NOT NULL, UNIQUE)
-
password (STRING, NOT NULL)
-
is_active (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
-
avatar (STRING, NULLABLE)
2. roles
-
id (PK, AUTO_INCREMENT)
-
name (STRING, NOT NULL)
-
is_active (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
3. role_users (Tabla pivote)
-
id (PK, AUTO_INCREMENT)
-
role_id (FK → roles.id)
-
user_id (FK → users.id)
-
is_active (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
4. resources
-
id (PK, AUTO_INCREMENT)
-
path (STRING, NOT NULL)
-
method (STRING, NOT NULL)
-
is_active (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
5. resource_roles (Tabla pivote)
-
id (PK, AUTO_INCREMENT)
-
resource_id (FK → resources.id)
-
role_id (FK → roles.id)
-
is_active (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
6. refresh_tokens
-
id (PK, AUTO_INCREMENT)
-
user_id (FK → users.id)
-
token (STRING, NOT NULL)
-
device_info (STRING, NOT NULL)
-
is_valid (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
-
expires_at (DATE)
-
created_at (DATE)
-
updated_at (DATE)
Tablas de Negocio
7. clients
-
id (PK, AUTO_INCREMENT)
-
name (STRING)
-
address (STRING)
-
phone (STRING)
-
email (STRING, UNIQUE)
-
password (STRING)
-
status (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
8. product_types
-
id (PK, AUTO_INCREMENT)
-
name (STRING, NOT NULL)
-
description (STRING, NOT NULL)
-
status (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
9. products
-
id (PK, AUTO_INCREMENT)
-
name (STRING, NOT NULL)
-
brand (STRING, NOT NULL)
-
price (BIGINT, NOT NULL)
-
min_stock (INTEGER, NOT NULL)
-
quantity (INTEGER, NOT NULL)
-
product_type_id (FK → product_types.id)
-
status (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
10. sales
-
id (PK, AUTO_INCREMENT)
-
sale_date (DATE, NOT NULL)
-
subtotal (BIGINT, NOT NULL)
-
tax (BIGINT, NOT NULL)
-
discounts (BIGINT, NOT NULL)
-
total (BIGINT, NOT NULL)
-
status (ENUM: "ACTIVE"/"INACTIVE", DEFAULT: "ACTIVE")
-
client_id (FK → clients.id)
11. product_sales (Tabla pivote)
-
id (PK, AUTO_INCREMENT)
-
total (BIGINT, NOT NULL)
-
product_id (FK → products.id)
-
sale_id (FK → sales.id)
Relaciones
Sistema de Autorización:
-
users ↔ role_users ↔ roles (Many-to-Many)
-
roles ↔ resource_roles ↔ resources (Many-to-Many)
-
users → refresh_tokens (One-to-Many)
Sistema de Negocio:
-
clients → sales (One-to-Many)
-
product_types → products (One-to-Many)
-
sales ↔ product_sales ↔ products (Many-to-Many)
Antes de crear los modelos de la aplicación es necesario que configuremos nuestra base de datos.
Especificación de la base de datos
Esto nos indica que procedemos a varios procesos.
Debe contar con la instalacion de los Motores de bases de datos:
-
MySQL Server
-
PosgreSQL
-
MS SQL Server
-
Oracle Server\
Paso 1: Configuración de Base de Datos
1.1 Instalar dependencias de base de datos
1.2 Crear archivo .env
PORT=3000
# Variable para seleccionar el motor de base de datos
DB_ENGINE=mysql
# Configuración para MySQL
MYSQL_HOST=localhost
MYSQL_USER=admin
MYSQL_PASSWORD=password
MYSQL_NAME=almacen_2025_iisem_node
MYSQL_PORT=3306
# Configuración para PostgreSQL
POSTGRES_HOST=localhost
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_NAME=almacen_2025_iisem_node
POSTGRES_PORT=5432
# Configuración para SQL Server
MSSQL_HOST=localhost
MSSQL_USER=sa
MSSQL_PASSWORD=password
MSSQL_NAME=almacen_2025_iisem_node
MSSQL_PORT=1433
# Configuración para Oracle
ORACLE_HOST=localhost
ORACLE_USER=ALMACENDB_ADMIN
ORACLE_PASSWORD=password
ORACLE_NAME=xe
ORACLE_PORT=1521
# JWT Secret
JWT_SECRET=your_jwt_secret_key_here
1.3 Crear configuración de base de datos
src/database/db.ts
import { Sequelize } from "sequelize";
import dotenv from "dotenv";
dotenv.config();
interface DatabaseConfig {
dialect: string;
host: string;
username: string;
password: string;
database: string;
port: number;
}
const dbConfigurations: Record<string, DatabaseConfig> = {
mysql: {
dialect: "mysql",
host: process.env.MYSQL_HOST || "localhost",
username: process.env.MYSQL_USER || "root",
password: process.env.MYSQL_PASSWORD || "",
database: process.env.MYSQL_NAME || "test",
port: parseInt(process.env.MYSQL_PORT || "3306")
},
postgres: {
dialect: "postgres",
host: process.env.POSTGRES_HOST || "localhost",
username: process.env.POSTGRES_USER || "postgres",
password: process.env.POSTGRES_PASSWORD || "",
database: process.env.POSTGRES_NAME || "test",
port: parseInt(process.env.POSTGRES_PORT || "5432")
}
};
const selectedEngine = process.env.DB_ENGINE || "mysql";
const selectedConfig = dbConfigurations[selectedEngine];
if (!selectedConfig) {
throw new Error(`Motor de base de datos no soportado: ${selectedEngine}`);
}
console.log(`🔌 Conectando a base de datos: ${selectedEngine.toUpperCase()}`);
export const sequelize = new Sequelize(
selectedConfig.database,
selectedConfig.username,
selectedConfig.password,
{
host: selectedConfig.host,
port: selectedConfig.port,
dialect: selectedConfig.dialect as any,
logging: process.env.NODE_ENV === 'development' ? console.log : false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
export const getDatabaseInfo = () => {
return {
engine: selectedEngine,
config: selectedConfig,
connectionString: `${selectedConfig.dialect}://${selectedConfig.username}@${selectedConfig.host}:${selectedConfig.port}/${selectedConfig.database}`
};
};
export const testConnection = async (): Promise<boolean> => {
try {
await sequelize.authenticate();
console.log(`✅ Conexión exitosa a ${selectedEngine.toUpperCase()}`);
return true;
} catch (error) {
console.error(`❌ Error de conexión a ${selectedEngine.toUpperCase()}:`, error);
return false;
}
};
Paso 2: Modelos de Base de Datos
2.1 Instalar dependencias de autenticación
2.2 Crear modelos de autorización
2.2.1 Modelo User
src/models/authorization/User.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
export class User extends Model {
id!: number;
public username!: string;
public email!: string;
public password!: string;
public is_active!: "ACTIVE" | "INACTIVE";
public avatar!: string;
public async checkPassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.password);
}
public generateToken(): string {
return jwt.sign({ id: this.id }, process.env.JWT_SECRET || 'secret', {
expiresIn: '10m',
});
}
public generateRefreshToken(): { token: string, expiresAt: Date } {
// const expiresIn = '24H';
const expiresIn = '5m';
const token = jwt.sign({ id: this.id }, process.env.JWT_SECRET || 'secret', {
expiresIn,
});
const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 1 minutos
// const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 horas
return { token, expiresAt };
}
}
export interface UserI {
id?: number;
username: string;
email: string;
password: string;
is_active: "ACTIVE" | "INACTIVE";
avatar?: string;
}
User.init(
{
username: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
},
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
avatar: {
type: DataTypes.STRING,
allowNull: true
}
},
{
tableName: "users",
sequelize: sequelize,
timestamps: false,
hooks: {
beforeCreate: async (user: User) => {
if (user.password) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
},
beforeUpdate: async (user: User) => {
if (user.password) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
}
}
}
);
2.2.2 Modelo Role
src/models/authorization/Role.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
export class Role extends Model {
public id!: number;
public name!: string;
public is_active!: "ACTIVE" | "INACTIVE";
}
export interface RoleI {
id?: number;
name: string;
is_active: "ACTIVE" | "INACTIVE";
}
Role.init(
{
name: {
type: DataTypes.STRING,
allowNull: false
},
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
}
},
{
tableName: "roles",
sequelize: sequelize,
timestamps: false
}
);
2.2.3 Modelo RoleUser (Modelo Pivote Relación muchos a muchos)
src/models/authorization/RoleUser.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
export class RoleUser extends Model {
public id!: number;
public role_id!: number;
public user_id!: number;
public is_active!: "ACTIVE" | "INACTIVE";
}
export interface RoleUserI {
id?: number;
role_id: number;
user_id: number;
is_active: "ACTIVE" | "INACTIVE";
}
RoleUser.init(
{
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
}
},
{
tableName: "role_users",
sequelize: sequelize,
timestamps: false
}
);
2.2.4 Modelo Resource
src/models/authorization/Resource.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
export class Resource extends Model {
public id!: number;
public path!: string;
public method!: string;
public is_active!: "ACTIVE" | "INACTIVE";
}
export interface ResourceI {
id?: number;
path: string;
method: string;
is_active: "ACTIVE" | "INACTIVE";
}
Resource.init(
{
path: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Path cannot be empty" },
},
},
method: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Method cannot be empty" },
},
},
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
tableName: "resources",
sequelize: sequelize,
timestamps: false,
}
);
2.2.5 Modelo ResourceRole(Modelo Pivote Relación muchos a muchos)
src/models/authorization/ResourceRole.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
export class ResourceRole extends Model {
public id!: number;
public resource_id!: number;
public role_id!: number;
public is_active!: "ACTIVE" | "INACTIVE";
}
export interface ResourceRoleI {
id?: number;
resource_id: number;
role_id: number;
is_active: "ACTIVE" | "INACTIVE";
}
ResourceRole.init(
{
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
tableName: "resource_roles",
sequelize: sequelize,
timestamps: false,
}
);
2.2.6 Modelo RefreshToken
src/models/authorization/ResfreshToken.ts
import { Model, DataTypes } from "sequelize";
import {sequelize} from "../../database/db";
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
export class RefreshToken extends Model {
id!: number;
public user_id!: number;
public token!: string;
public device_info!: string;
public is_valid!: "ACTIVE" | "INACTIVE";;
public expires_at!: Date;
public created_at!: Date;
public updated_at!: Date;
}
export interface RefreshTokenI {
user_id?: number;
token: string;
device_info: string;
is_valid: "ACTIVE" | "INACTIVE";
expires_at: Date;
created_at: Date;
updated_at: Date;
}
RefreshToken.init(
{
token: {
type: DataTypes.STRING,
allowNull: false
},
device_info: {
type: DataTypes.STRING,
allowNull: false
},
is_valid: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
expires_at: {
type: DataTypes.DATE,
allowNull: true
},
created_at: {
type: DataTypes.DATE,
allowNull: true
},
updated_at: {
type: DataTypes.DATE,
allowNull: true
}
},
{
tableName: "refresh_tokens",
sequelize: sequelize,
timestamps: false,
hooks: {
beforeCreate: (refreshToken: RefreshToken) => {
const currentDate = new Date();
refreshToken.created_at = currentDate;
refreshToken.updated_at = currentDate;
},
beforeUpdate: (refreshToken: RefreshToken) => {
const currentDate = new Date();
refreshToken.updated_at = currentDate;
}
}
}
);
2.3 Crear modelos de negocio
2.3.1 Modelo Client
src/models/Client.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
import bcrypt from 'bcryptjs';
export interface ClientI {
id?: number;
name: string;
address: string;
phone: string;
email: string;
password: string;
status: "ACTIVE" | "INACTIVE";
}
export class Client extends Model {
public id!: number;
public name!: string;
public address!: string;
public phone!: string;
public email!: string;
public password!: string;
public status!: "ACTIVE" | "INACTIVE";
}
Client.init(
{
name: {
type: DataTypes.STRING,
allowNull: true,
},
address: {
type: DataTypes.STRING,
allowNull: true,
},
phone: {
type: DataTypes.STRING,
allowNull: true,
validate: {
notEmpty: { msg: "Phone cannot be empty" },
},
},
email: {
type: DataTypes.STRING,
allowNull: true,
unique: true,
validate: {
isEmail: { msg: "Email must be a valid email address" }, // Validate email format
},
},
password: {
type: DataTypes.STRING,
allowNull: true,
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "Client",
tableName: "clients",
timestamps: false,
hooks: {
beforeCreate: async (client: Client) => {
if (client.password) {
const salt = await bcrypt.genSalt(10);
client.password = await bcrypt.hash(client.password, salt);
}
},
beforeUpdate: async (client: Client) => {
if (client.password) {
const salt = await bcrypt.genSalt(10);
client.password = await bcrypt.hash(client.password, salt);
}
}
}
}
);
2.3.2 Modelo ProductType
src/models/ProductType.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
export interface ProductTypeI {
id?: number;
name: string;
description: string;
status: "ACTIVE" | "INACTIVE";
}
export class ProductType extends Model {
public id!: number;
public name!: string;
public description!: string;
public status!: "ACTIVE" | "INACTIVE";
}
ProductType.init(
{
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Name cannot be empty" },
},
},
description: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Description cannot be empty" },
},
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "ProductType",
tableName: "product_types",
timestamps: false,
}
);
2.3.3 Modelo Product
src/models/Product.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
export interface ProductI {
id?: number;
name: string;
brand: string;
price: number;
min_stock: number;
quantity: number;
product_type_id: number;
status: "ACTIVE" | "INACTIVE";
}
export class Product extends Model {
public id!: number;
public name!: string;
public brand!: string;
public price!: number;
public min_stock!: number;
public quantity!: number;
public product_type_id!: number;
public status!: "ACTIVE" | "INACTIVE";
}
Product.init(
{
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Name cannot be empty" },
},
},
brand: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: { msg: "Brand cannot be empty" },
},
},
price: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Price cannot be empty" },
isFloat: { msg: "Price must be a valid number" },
},
},
min_stock: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notEmpty: { msg: "Minimum stock cannot be empty" },
isInt: { msg: "Minimum stock must be an integer" },
},
},
quantity: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
notEmpty: { msg: "Quantity cannot be empty" },
isInt: { msg: "Quantity must be an integer" },
},
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "Product",
tableName: "products",
timestamps: false,
}
);
2.3.4 Modelo Sale
src/models/Sale.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
export interface SaleI {
id?: number;
sale_date: Date;
subtotal: number;
tax: number;
discounts: number;
total: number;
status: "ACTIVE" | "INACTIVE";
client_id: number;
}
export class Sale extends Model {
public id!: number;
public sale_date!: Date;
public subtotal!: number;
public tax!: number;
public discounts!: number;
public total!: number;
public status!: "ACTIVE" | "INACTIVE";
public client_id!: number;
}
Sale.init(
{
sale_date: {
type: DataTypes.DATE,
allowNull: false,
validate: {
notEmpty: { msg: "Sale date cannot be empty" },
isDate: { args: true, msg: "Must be a valid date" },
},
},
subtotal: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Subtotal cannot be empty" },
isFloat: { msg: "Subtotal must be a valid number" },
},
},
tax: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Tax cannot be empty" },
isFloat: { msg: "Tax must be a valid number" },
},
},
discounts: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Discounts cannot be empty" },
isFloat: { msg: "Discounts must be a valid number" },
},
},
total: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Total cannot be empty" },
isFloat: { msg: "Total must be a valid number" },
},
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "Sale",
tableName: "sales",
timestamps: false,
}
);
2.3.5 Modelo ProductSale
src/models/ProductSale.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
export interface ProductSaleI {
id?: number;
total: number;
product_id: number;
sale_id: number;
}
export class ProductSale extends Model {
public id!: number;
public total!: number;
public product_id!: number;
public sale_id!: number;
}
ProductSale.init(
{
total: {
type: DataTypes.BIGINT,
allowNull: false,
validate: {
notEmpty: { msg: "Name cannot be empty" },
},
},
},
{
sequelize,
modelName: "ProductSale",
tableName: "product_sales",
timestamps: false,
}
);
2.4 Configurar relaciones entre modelos
Agregar al final de cada archivo de modelo las relaciones correspondientes y se tienen en cuenta las relaciones en ambos sentidos
// En User.ts
....
....
....
import { RoleUser } from "./RoleUser";
...
...
...
Al final del archivo
User.hasMany(RoleUser, {
foreignKey: 'user_id',
sourceKey: "id",
});
RoleUser.belongsTo(User, {
foreignKey: 'user_id',
targetKey: "id",
});
Quedando finalmente el archivo de la siguiente manera:
src/models/User.ts
import { Model, DataTypes } from "sequelize";
import { sequelize } from "../../database/db";
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { RoleUser } from "./RoleUser";
export class User extends Model {
id!: number;
public username!: string;
public email!: string;
public password!: string;
public is_active!: "ACTIVE" | "INACTIVE";
public avatar!: string;
public async checkPassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.password);
}
public generateToken(): string {
return jwt.sign({ id: this.id }, process.env.JWT_SECRET || 'secret', {
expiresIn: '10m',
});
}
public generateRefreshToken(): { token: string, expiresAt: Date } {
// const expiresIn = '24H';
const expiresIn = '5m';
const token = jwt.sign({ id: this.id }, process.env.JWT_SECRET || 'secret', {
expiresIn,
});
const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 1 minutos
// const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 horas
return { token, expiresAt };
}
}
export interface UserI {
id?: number;
username: string;
email: string;
password: string;
is_active: "ACTIVE" | "INACTIVE";
avatar?: string;
}
User.init(
{
username: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
},
is_active: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
avatar: {
type: DataTypes.STRING,
allowNull: true
}
},
{
tableName: "users",
sequelize: sequelize,
timestamps: false,
hooks: {
beforeCreate: async (user: User) => {
if (user.password) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
},
beforeUpdate: async (user: User) => {
if (user.password) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
}
}
}
);
User.hasMany(RoleUser, {
foreignKey: 'user_id',
sourceKey: "id",
});
RoleUser.belongsTo(User, {
foreignKey: 'user_id',
targetKey: "id",
});
De la misma manera realizar las distintas relaciones mencionadas a continuacion:
// En Role.ts
....
....
....
import { RoleUser } from "./RoleUser";
...
...
...
Al final del archivo
Role.hasMany(RoleUser, {
foreignKey: 'role_id',
sourceKey: "id",
});
RoleUser.belongsTo(Role, {
foreignKey: 'role_id',
targetKey: "id",
});
___________________________________________________________________________________
// En ResourceRole.ts
....
....
....
import { Resource } from "./Resource";
import { Role } from "./Role";
...
...
...
Al final del archivo
Resource.hasMany(ResourceRole, {
foreignKey: "resource_id",
sourceKey: "id",
});
ResourceRole.belongsTo(Resource, {
foreignKey: "resource_id",
targetKey: "id",
});
Role.hasMany(ResourceRole, {
foreignKey: "role_id",
sourceKey: "id",
});
ResourceRole.belongsTo(Role, {
foreignKey: "role_id",
targetKey: "id",
});
__________________________________________________________________________________
// En RefreshToken.ts
....
....
....
import { User } from "./User";
...
...
...
Al final del archivo
User.hasMany(RefreshToken, {
foreignKey: 'user_id',
sourceKey: "id",
});
RefreshToken.belongsTo(User, {
foreignKey: 'user_id',
targetKey: "id",
});
_________________________________________________________________________________
// En Client.ts
....
....
....
import { Sale } from "./Sale";
...
...
...
Al final del archivo
Client.hasMany(Sale, {
foreignKey: "client_id",
sourceKey: "id",
});
Sale.belongsTo(Client, {
foreignKey: "client_id",
targetKey: "id",
});
_________________________________________________________________________________
// En ProductType.ts
....
....
....
import { Product } from "./Product";
...
...
...
Al final del archivo
ProductType.hasMany(Product, {
foreignKey: "product_type_id",
sourceKey: "id",
});
Product.belongsTo(ProductType, {
foreignKey: "product_type_id",
targetKey: "id",
});
___________________________________________________________________________________
// En Product.ts
....
....
....
import { ProductSale } from "./ProductSale";
...
...
...
Al final del archivo
Product.hasMany(ProductSale, {
foreignKey: "product_id",
sourceKey: "id",
});
ProductSale.belongsTo(Product, {
foreignKey: "product_id",
targetKey: "id",
});
_________________________________________________________________________________
// En Sale.ts
....
....
....
import { ProductSale } from "./ProductSale";
...
...
...
Al final del archivo
Sale.hasMany(ProductSale, {
foreignKey: "sale_id",
sourceKey: "id",
});
ProductSale.belongsTo(Sale, {
foreignKey: "sale_id",
targetKey: "id",
});
Paso 3: Creacion, conexion y sincronizacion de la base de datos
Es importante saber que en el archivo .env se encuentran los datos de conexion y nombre de la base de datos, por ende se be crear las bases de datos con dicho nombre en los motores de base de datos indicados:
Por ejemplo en MySQL
# Configuración para MySQL
MYSQL_HOST=localhost
MYSQL_USER=admin
MYSQL_PASSWORD=MiNiCo57**
MYSQL_NAME=almacen_2025_iisem_node
MYSQL_PORT=3306
Para mas informacion revisar archivo .env
Esto quiere decir que los motores deben existir al igual que las bases de datos en blanco
Luego se debe sincronizar en la configuracion asi:
src/config/index.ts
Agregar el metodo dbConnection
import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize, testConnection, getDatabaseInfo } from "../database/db";
var cors = require("cors");
dotenv.config();
export class App {
public app: Application;
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 }));
}
private routes(): void {
// Las rutas se configurarán más adelante
}
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')}`);
}
}
Una vez realizado estos cambios, ejecutamos con
npm run dev
