Express
Tutorial Express: Introducción a ORM Sequelize
Aprende a instalar, configurar Sequelize y definir modelos básicos para gestionar bases de datos en Express con buenas prácticas y validaciones.
Aprende Express y certifícateInstalación y configuración Sequelize
Sequelize es un ORM (Object-Relational Mapping) que nos permite trabajar con bases de datos relacionales usando JavaScript en lugar de escribir consultas SQL directamente. En el contexto de Express, Sequelize actúa como una capa de abstracción que facilita la comunicación entre nuestra aplicación y la base de datos.
Un ORM traduce los datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y una base de datos relacional. Esto significa que podemos trabajar con objetos JavaScript para representar filas de tablas, y Sequelize se encarga de convertir nuestras operaciones en consultas SQL apropiadas.
Instalación de dependencias
Para comenzar a trabajar con Sequelize en nuestro proyecto Express, necesitamos instalar tanto Sequelize como el driver específico para nuestra base de datos. En este ejemplo utilizaremos PostgreSQL, aunque Sequelize también soporta MySQL, MariaDB, SQLite y Microsoft SQL Server.
npm install sequelize pg pg-hstore
El paquete pg
es el driver de PostgreSQL para Node.js, mientras que pg-hstore
permite trabajar con el tipo de datos hstore de PostgreSQL. Si prefieres usar otra base de datos, deberás instalar su driver correspondiente:
# Para MySQL/MariaDB
npm install mysql2
# Para SQLite
npm install sqlite3
# Para SQL Server
npm install tedious
También es recomendable instalar Sequelize CLI como dependencia de desarrollo para facilitar tareas como migraciones y generación de modelos:
npm install --save-dev sequelize-cli
Configuración inicial de Sequelize
Una vez instaladas las dependencias, necesitamos configurar la conexión a nuestra base de datos. Crea un archivo database.js
en la carpeta config
de tu proyecto Express:
// config/database.js
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME || 'mi_aplicacion',
username: process.env.DB_USER || 'usuario',
password: process.env.DB_PASSWORD || 'contraseña',
logging: process.env.NODE_ENV === 'development' ? console.log : false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
module.exports = sequelize;
Esta configuración utiliza variables de entorno para mantener segura la información sensible como credenciales de base de datos. El objeto pool
define la configuración del pool de conexiones, que optimiza el rendimiento al reutilizar conexiones existentes.
Configuración de variables de entorno
Crea un archivo .env
en la raíz de tu proyecto para definir las variables de entorno necesarias:
# .env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mi_aplicacion_dev
DB_USER=mi_usuario
DB_PASSWORD=mi_contraseña
NODE_ENV=development
Para cargar estas variables en tu aplicación Express, instala y configura dotenv:
npm install dotenv
Luego, carga las variables al inicio de tu aplicación principal:
// app.js
require('dotenv').config();
const express = require('express');
const sequelize = require('./config/database');
const app = express();
// Middleware y rutas...
module.exports = app;
Verificación de la conexión
Es importante verificar que la conexión a la base de datos funciona correctamente. Añade esta función de verificación en tu archivo principal:
// app.js
const testConnection = async () => {
try {
await sequelize.authenticate();
console.log('✅ Conexión a la base de datos establecida correctamente');
} catch (error) {
console.error('❌ Error al conectar con la base de datos:', error.message);
process.exit(1);
}
};
// Verificar conexión al iniciar la aplicación
testConnection();
Sincronización con la base de datos
Sequelize puede crear automáticamente las tablas basándose en los modelos que definamos. Para configurar la sincronización, añade el siguiente código:
// app.js
const syncDatabase = async () => {
try {
// En desarrollo, sincroniza los modelos con la base de datos
if (process.env.NODE_ENV === 'development') {
await sequelize.sync({ alter: true });
console.log('📊 Modelos sincronizados con la base de datos');
}
} catch (error) {
console.error('❌ Error al sincronizar modelos:', error.message);
}
};
// Llamar después de verificar la conexión
testConnection().then(() => {
syncDatabase();
});
La opción alter: true
permite que Sequelize modifique las tablas existentes para que coincidan con los modelos, lo cual es útil durante el desarrollo. En producción, es recomendable usar migraciones en lugar de sincronización automática.
Estructura de archivos recomendada
Para mantener el código organizado, es recomendable seguir esta estructura de archivos:
proyecto/
├── config/
│ └── database.js
├── models/
│ └── index.js
├── routes/
├── .env
├── .gitignore
└── app.js
Crea un archivo models/index.js
que centralice la configuración de Sequelize:
// models/index.js
const sequelize = require('../config/database');
// Aquí importaremos todos los modelos cuando los creemos
const models = {};
// Configurar asociaciones entre modelos
Object.keys(models).forEach(modelName => {
if (models[modelName].associate) {
models[modelName].associate(models);
}
});
module.exports = {
sequelize,
...models
};
Configuración para diferentes entornos
Es importante tener configuraciones específicas para desarrollo, testing y producción. Crea un archivo config/config.js
:
// config/config.js
module.exports = {
development: {
dialect: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME || 'mi_app_dev',
username: process.env.DB_USER || 'usuario',
password: process.env.DB_PASSWORD || 'contraseña',
logging: console.log
},
test: {
dialect: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME_TEST || 'mi_app_test',
username: process.env.DB_USER || 'usuario',
password: process.env.DB_PASSWORD || 'contraseña',
logging: false
},
production: {
dialect: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
logging: false,
ssl: true,
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false
}
}
}
};
Con esta configuración base, Sequelize está listo para integrarse con tu aplicación Express. La conexión está establecida, las variables de entorno configuradas, y el sistema preparado para definir modelos que representen las tablas de tu base de datos.
Modelos básicos
Los modelos en Sequelize representan las tablas de nuestra base de datos como clases JavaScript. Cada modelo define la estructura de una tabla, incluyendo sus columnas, tipos de datos, validaciones y relaciones con otros modelos. Esto nos permite trabajar con datos de forma orientada a objetos en lugar de escribir consultas SQL directamente.
Un modelo actúa como una plantilla que describe cómo se almacenan y organizan los datos. Por ejemplo, si tenemos una tabla de usuarios, el modelo User definirá qué campos tiene cada usuario (nombre, email, contraseña) y qué reglas deben cumplir estos datos.
Definición de un modelo básico
Para crear nuestro primer modelo, definamos un modelo User
que represente usuarios de nuestra aplicación. Crea un archivo models/User.js
:
// models/User.js
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
nombre: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: true,
len: [2, 100]
}
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
edad: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {
min: 0,
max: 120
}
},
activo: {
type: DataTypes.BOOLEAN,
defaultValue: true
}
}, {
tableName: 'usuarios',
timestamps: true
});
module.exports = User;
En este modelo definimos varios tipos de datos importantes. El campo id
es la clave primaria que se incrementa automáticamente. Los campos nombre
y email
son obligatorios (allowNull: false
), mientras que edad
es opcional. El campo activo
tiene un valor por defecto de true
.
Tipos de datos fundamentales
Sequelize ofrece múltiples tipos de datos que se mapean a los tipos SQL correspondientes:
// Ejemplos de tipos de datos comunes
const Producto = sequelize.define('Producto', {
// Tipos de texto
nombre: DataTypes.STRING, // VARCHAR(255)
descripcion: DataTypes.TEXT, // TEXT
codigo: DataTypes.STRING(50), // VARCHAR(50)
// Tipos numéricos
precio: DataTypes.DECIMAL(10, 2), // DECIMAL(10,2)
stock: DataTypes.INTEGER, // INTEGER
peso: DataTypes.FLOAT, // FLOAT
// Tipos de fecha
fechaCreacion: DataTypes.DATE, // DATETIME
fechaSolo: DataTypes.DATEONLY, // DATE
// Tipos booleanos y otros
disponible: DataTypes.BOOLEAN, // BOOLEAN
metadatos: DataTypes.JSON // JSON (PostgreSQL)
});
Validaciones en modelos
Las validaciones aseguran que los datos cumplan ciertas reglas antes de guardarse en la base de datos. Sequelize incluye validaciones integradas y permite crear validaciones personalizadas:
const Usuario = sequelize.define('Usuario', {
username: {
type: DataTypes.STRING(30),
allowNull: false,
unique: true,
validate: {
len: [3, 30], // Longitud entre 3 y 30 caracteres
isAlphanumeric: true, // Solo letras y números
notContains: ' ' // No puede contener espacios
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [8, 100], // Mínimo 8 caracteres
is: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/ // Regex personalizada
}
},
telefono: {
type: DataTypes.STRING(15),
validate: {
isNumeric: true, // Solo números
len: [9, 15] // Entre 9 y 15 dígitos
}
}
});
Configuración de timestamps
Por defecto, Sequelize añade automáticamente los campos createdAt
y updatedAt
a todos los modelos. Puedes personalizar este comportamiento:
const Articulo = sequelize.define('Articulo', {
titulo: DataTypes.STRING,
contenido: DataTypes.TEXT
}, {
timestamps: true, // Habilitar timestamps (por defecto)
createdAt: 'fechaCreacion', // Personalizar nombre del campo
updatedAt: 'fechaActualizacion',
// O deshabilitar timestamps completamente
// timestamps: false
});
Operaciones básicas con modelos
Una vez definido el modelo, podemos realizar operaciones CRUD básicas. Aquí algunos ejemplos de cómo usar el modelo en nuestras rutas Express:
// routes/users.js
const express = require('express');
const User = require('../models/User');
const router = express.Router();
// Crear un nuevo usuario
router.post('/', async (req, res) => {
try {
const nuevoUsuario = await User.create({
nombre: req.body.nombre,
email: req.body.email,
edad: req.body.edad
});
res.status(201).json(nuevoUsuario);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Obtener todos los usuarios
router.get('/', async (req, res) => {
try {
const usuarios = await User.findAll();
res.json(usuarios);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Obtener un usuario por ID
router.get('/:id', async (req, res) => {
try {
const usuario = await User.findByPk(req.params.id);
if (!usuario) {
return res.status(404).json({ error: 'Usuario no encontrado' });
}
res.json(usuario);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;
Registro de modelos en la aplicación
Para que nuestros modelos estén disponibles en toda la aplicación, debemos registrarlos en el archivo models/index.js
:
// models/index.js
const sequelize = require('../config/database');
const User = require('./User');
// Registrar todos los modelos
const models = {
User
};
// Configurar asociaciones (para futuras relaciones)
Object.keys(models).forEach(modelName => {
if (models[modelName].associate) {
models[modelName].associate(models);
}
});
module.exports = {
sequelize,
...models
};
Buenas prácticas para modelos
Al definir modelos, es importante seguir ciertas convenciones que faciliten el mantenimiento del código:
Nombres en singular: Los modelos se nombran en singular (
User
, noUsers
), ya que representan una instancia individual.Campos obligatorios: Define claramente qué campos son obligatorios usando
allowNull: false
.Validaciones apropiadas: Incluye validaciones que reflejen las reglas de negocio de tu aplicación.
Índices únicos: Usa
unique: true
para campos que deben ser únicos como emails o usernames.
const Cliente = sequelize.define('Cliente', {
dni: {
type: DataTypes.STRING(9),
allowNull: false,
unique: true,
validate: {
len: [9, 9],
isAlphanumeric: true
}
},
nombre: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: true
}
},
fechaNacimiento: {
type: DataTypes.DATEONLY,
validate: {
isDate: true,
isBefore: new Date().toISOString() // No puede ser fecha futura
}
}
}, {
tableName: 'clientes',
indexes: [
{
unique: true,
fields: ['dni']
}
]
});
Con estos fundamentos, ya puedes crear modelos que representen las entidades principales de tu aplicación Express. Los modelos proporcionan una interfaz limpia y segura para interactuar con la base de datos, manejando automáticamente la validación de datos y la conversión entre objetos JavaScript y registros SQL.
Otras lecciones de Express
Accede a todas las lecciones de Express y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Expressjs
Introducción Y Entorno
Instalación De Express
Introducción Y Entorno
Estados Http
Routing
Métodos Delete
Routing
Parámetros Y Query Strings
Routing
Métodos Get
Routing
Ejercicios de programación de Express
Evalúa tus conocimientos de esta lección Introducción a ORM Sequelize con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.