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.
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.
-
Instalar el motor de la base de datos que para este caso es MySql.
https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-database
-
Una vez instalado el motor debemos iniciar dicho motor.
Abrimos la terminal de Ubuntu WSL y escribimos.
sudo /etc/init.d/mysql start
Si aparece este mensaje, quiere decir que ya esta inicializado.
* Starting MySQL database server mysqld
Crear la base de datos indicada: 'NAME': 'almacen2022_mysql_nodejs',
para ello, en la terminal de Ubuntu WSL ingresar
sudo mysql -u root -p
Y luego la contraseña.
Si aparece en la terminal
mysql> Indica que ya entró al motor de MySQL.
Luego realice:
show databases;
Para mostrar las bases de datos que tiene creadas
create database almacen2022_mysql_nodejs;
Debe aparecer en pantalla, para indicar que ha sido creada.
Query OK, 1 row affected (0.01 sec)
Una vez creada la base de datos físicamente y haber levantado el servidor, procedemos a los modelos.
Primeramente instalamos las librerias que me permiten conexion con los motores de las bases de datos y tambien instalamos para utilizar el archivo .env y enviar datos externoas a las conexiones.
Luego, creamos archivo src/database/db.ts
import { Sequelize } from "sequelize";
import dotenv from "dotenv";
dotenv.config();
const dbConfig = {
mysql: {
dialect: "mysql",
host: process.env.DB_HOST || "localhost",
username: process.env.DB_USER || "root",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "test",
port: parseInt(process.env.DB_PORT || "3306")
},
postgres: {
dialect: "postgres",
host: process.env.DB_HOST || "localhost",
username: process.env.DB_USER || "postgres",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "test",
port: parseInt(process.env.DB_PORT || "5432")
},
mssql: {
dialect: "mssql",
host: process.env.DB_HOST || "localhost",
username: process.env.DB_USER || "sa",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "test",
port: parseInt(process.env.DB_PORT || "1433")
},
oracle: {
dialect: "oracle",
host: process.env.DB_HOST || "localhost",
username: process.env.DB_USER || "oracle",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "test",
port: parseInt(process.env.DB_PORT || "1521")
},
};
// Select the database engine from the .env file
const dbEngine = process.env.DB_ENGINE || "mysql";
const config = dbConfig[dbEngine as keyof typeof dbConfig];
if (!config) {
throw new Error(`Unsupported DB_ENGINE: ${dbEngine}`);
}
// Create the Sequelize instance
export const sequelize = new Sequelize(config.database, config.username, config.password, {
host: config.host,
dialect: config.dialect as any,
});
Crear archivo backend-profe/.env
PORT=3000
DB_ENGINE=mysql
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=MiNiCo57**
DB_NAME=almacen_2025_isem_node
DB_PORT=3306
# DB_ENGINE=postgres
# DB_HOST=localhost
# DB_USER=postgres
# DB_PASSWORD=MiNiCo57**
# DB_NAME=almacen_2025_isem_node
# DB_PORT=5432
# DB_ENGINE=mssql
# DB_HOST=localhost
# DB_USER=sa
# DB_PASSWORD=MiNiCo57**
# DB_NAME=almacen_2025_isem_node
# DB_PORT=1433
# DB_ENGINE=oracle
# DB_HOST=localhost
# DB_USER=ALMACENDB_ADMIN
# DB_PASSWORD=MiNiCo57
# DB_NAME=xe
# DB_PORT=1521
y se complementa el codigo en config/index.ts
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize } from "../database/db";
// Load environment variables from the .env file
export class App {
public app: Application;
constructor(private port?: number | string) {
this.app = express();
this.settings();
this.middlewares();
this.dbConnection(); // Call the database connection method
}
// Application settings
private settings(): void {
this.app.set('port', this.port || process.env.PORT || 4000);
}
// Middleware configuration
private middlewares(): void {
this.app.use(morgan('dev'));
this.app.use(express.json()); // leer json raw
this.app.use(express.urlencoded({ extended: false })); //leer json form
}
// Method to connect and synchronize the database
private async dbConnection(): Promise<void> {
try {
await sequelize.sync({ force: true }); // Synchronize the database
console.log("Database connected successfully");
} catch (error) {
console.error("Unable to connect to the database:", error);
}
}
// Start the server
async listen() {
await this.app.listen(this.app.get('port'));
console.log('Server on port', this.app.get('port'));
}
}
Seguidamente, se procede a crear los modelos.
Nota: Se recomienda iniciar por aquellos modelos que no tengan llaves foráneas.
Modelo Client
Crear el archivo con el nombre del Modelo en src/models/Client.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
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" },
len: { args: [10, 15], msg: "Phone must be between 10 and 15 characters" },
},
},
email: {
type: DataTypes.STRING,
allowNull: true,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: true,
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "Client",
tableName: "clients",
timestamps: false,
}
);
Enlace para conocer los tipos de datos de Sequelize
https://sequelize.org/docs/v7/other-topics/other-data-types/
Controlador Client
Crear el archivo 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" });
}
}
}
Rutas de Cliente
Crear el archivo 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 {
app.route("/clientes").get(this.clientController.getAllClients);
app.route("/clientes/:id").get(this.clientController.getClientById);
}
}
Luego indexamos esta rutas en
En caso que no exista, crear el archivo src/routes/index.ts
import { Router } from "express";
import { ClientRoutes } from "./client";
export class Routes {
public clientRoutes: ClientRoutes = new ClientRoutes();
}
Definir Cliente en Config
Primeramente debemos instalar el paquete de Cors para que pueda hacer peticiones desde el cliente
posteriormente, luego en el archivo src/config/index.ts
dicho archivo queda finalmente de esta manera:
import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize } from "../database/db";
import { Routes } from "../routes/index";
// Load environment variables from the .env file
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(); // Call the database connection method
}
// Application settings
private settings(): void {
this.app.set('port', this.port || process.env.PORT || 4000);
}
// Middleware configuration
private middlewares(): void {
this.app.use(morgan('dev'));
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
}
// Route configuration
private routes(): void {
this.routePrv.clientRoutes.routes(this.app);
}
// Method to connect and synchronize the database
private async dbConnection(): Promise<void> {
try {
await sequelize.sync({ force: true }); // Synchronize the database
console.log("Database connected successfully");
} catch (error) {
console.error("Unable to connect to the database:", error);
}
}
// Start the server
async listen() {
await this.app.listen(this.app.get('port'));
console.log('Server on port', this.app.get('port'));
}
}
Ahora hacemos lo mismo con el modelo Sale
Modelo Sale
Crear el archivo con el nombre del Modelo en src/models/Sale.ts
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
export interface SaleI {
id?: number;
saleDate: Date;
subtotal: number;
tax: number;
discounts: number;
total: number;
status: "ACTIVE" | "INACTIVE";
client_id: number;
sale_id: number;
}
export class Sale extends Model {
public id!: number;
public saleDate!: Date;
public subtotal!: number;
public tax!: number;
public discounts!: number;
public total!: number;
public status!: "ACTIVE" | "INACTIVE";
public client_id!: number;
public sale_id!: number;
}
Sale.init(
{
saleDate: {
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.FLOAT,
allowNull: false,
validate: {
notEmpty: { msg: "Subtotal cannot be empty" },
isFloat: { msg: "Subtotal must be a valid number" },
},
},
tax: {
type: DataTypes.FLOAT,
allowNull: false,
validate: {
notEmpty: { msg: "Tax cannot be empty" },
isFloat: { msg: "Tax must be a valid number" },
},
},
discounts: {
type: DataTypes.FLOAT,
allowNull: false,
validate: {
notEmpty: { msg: "Discounts cannot be empty" },
isFloat: { msg: "Discounts must be a valid number" },
},
},
total: {
type: DataTypes.FLOAT,
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,
}
);
Controlador Sale
Crear el archivo 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" });
}
}
}
Rutas de Cliente
Crear el archivo 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 {
app.route("/ventas").get(this.saleController.getAllSales); // Get all sales
app.route("/ventas/:id").get(this.saleController.getSaleById); // Get a sale by ID
}
}
Luego indexamos esta rutas en
En caso que no exista, crear el archivo src/routes/index.ts
import { Router } from "express";
import { ClientRoutes } from "./client";
import { SaleRoutes } from "./sale";
export class Routes {
public clientRoutes: ClientRoutes = new ClientRoutes();
public saleRoutes: SaleRoutes = new SaleRoutes();
}
Definir Sale en Config
Primeramente debemos instalar el paquete de Cors para que pueda hacer peticiones desde sale
posteriormente, luego en el archivo src/config/index.ts
dicho archivo queda finalmente de esta manera:
import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize } from "../database/db";
import { Routes } from "../routes/index";
// Load environment variables from the .env file
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(); // Call the database connection method
}
// Application settings
private settings(): void {
this.app.set('port', this.port || process.env.PORT || 4000);
}
// Middleware configuration
private middlewares(): void {
this.app.use(morgan('dev'));
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);
}
// Method to connect and synchronize the database
private async dbConnection(): Promise<void> {
try {
await sequelize.sync({ force: true }); // Synchronize the database
console.log("Database connected successfully");
} catch (error) {
console.error("Unable to connect to the database:", error);
}
}
// Start the server
async listen() {
await this.app.listen(this.app.get('port'));
console.log('Server on port', this.app.get('port'));
}
}
Si existen relaciones adicionar las relaciones en uno de los modelos, indicando el nombre de la llave foranea, en el caso de clientes y sale lo haremos en el modelo Client.
import { Sale } from "./Sale";
....
....
....
Client.hasMany(Sale, {
foreignKey: "client_id",
sourceKey: "id",
});
Sale.belongsTo(Client, {
foreignKey: "client_id",
targetKey: "id",
});
Codigo completo
import { DataTypes, Model } from "sequelize";
import { sequelize } from "../database/db";
import { Sale } from "./Sale";
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" },
len: { args: [10, 15], msg: "Phone must be between 10 and 15 characters" },
},
},
email: {
type: DataTypes.STRING,
allowNull: true,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: true,
},
status: {
type: DataTypes.ENUM("ACTIVE", "INACTIVE"),
defaultValue: "ACTIVE",
},
},
{
sequelize,
modelName: "Client",
tableName: "clients",
timestamps: false,
}
);
Client.hasMany(Sale, {
foreignKey: "client_id",
sourceKey: "id",
});
Sale.belongsTo(Client, {
foreignKey: "client_id",
targetKey: "id",
});
De la misma forma haremos con el resto de los modelos