Sequelize ORM with MySQL Setup: Complete Guide
Sequelize is a popular ORM (Object-Relational Mapping) for Node.js that provides a clean interface for database operations. In this guide, we'll set up Sequelize with MySQL using a singleton pattern for connection management and production-ready configuration.
Sequelize is a popular ORM (Object-Relational Mapping) for Node.js that provides a clean interface for database operations. In this guide, we'll set up Sequelize with MySQL using a singleton pattern for connection management and production-ready configuration.
Installation
npm install sequelize mysql2
npm install dotenvDatabase Connection with Singleton Pattern
Creating a singleton database connection class:
const { Sequelize } = require("sequelize");
require("dotenv").config();
class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.sequelize = new Sequelize(
process.env.DB_NAME || "inventory_management",
process.env.DB_USER || "root",
process.env.DB_PASSWORD || "",
{
host: process.env.DB_HOST || "localhost",
port: process.env.DB_PORT || 3306,
dialect: "mysql",
// Connection Pool Configuration
pool: {
max: 10, // Maximum connections
min: 0, // Minimum connections
acquire: 30000, // Max time to get connection
idle: 10000, // Max time connection can be idle
},
// Logging
logging: process.env.NODE_ENV === "production" ? false : console.log,
// Retry Configuration
retry: {
max: 3,
match: [
Sequelize.ConnectionError,
Sequelize.ConnectionTimedOutError,
],
},
// Query timeout
dialectOptions: {
connectTimeout: 60000,
decimalNumbers: true,
},
// Timezone
timezone: "+00:00",
// Model defaults
define: {
timestamps: true,
underscored: false,
freezeTableName: true,
charset: "utf8mb4",
collate: "utf8mb4_unicode_ci",
},
}
);
Database.instance = this;
}
async testConnection() {
try {
await this.sequelize.authenticate();
console.log("✅ MySQL Database connected successfully");
return true;
} catch (error) {
console.error("❌ Database connection failed:", error.message);
return false;
}
}
async closeConnection() {
try {
await this.sequelize.close();
console.log("✅ Database connection closed");
return true;
} catch (error) {
console.error("❌ Error closing connection:", error.message);
return false;
}
}
}
const database = new Database();
module.exports = database;Creating Models
Defining a Product model with associations:
const { DataTypes } = require("sequelize");
const database = require("./database");
const { Category } = require("./index");
const Product = database.getSequelize().define("Product", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: Category,
key: "id",
},
},
sku: {
type: DataTypes.STRING,
allowNull: true,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0,
},
cost: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0,
},
stock: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
minStock: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
unit: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "pcs",
},
barcode: {
type: DataTypes.STRING,
allowNull: true,
},
}, {
tableName: "products",
timestamps: true,
});
// Associations
Product.belongsTo(Category, { foreignKey: "categoryId", as: "category" });
Category.hasMany(Product, { foreignKey: "categoryId", as: "products" });
module.exports = Product;Model Class for Clean Interface
Creating a model class that wraps Sequelize operations:
const { Product, Category } = require("./index");
class ProductModel {
async getAll() {
try {
const products = await Product.findAll({
include: [{
model: Category,
as: "category",
attributes: ["id", "name"],
}],
order: [["createdAt", "DESC"]],
});
return products.map(product => {
const data = product.toJSON();
return {
...data,
categoryName: data.category ? data.category.name : "Unknown",
};
});
} catch (error) {
console.error("Error getting all products:", error);
return [];
}
}
async getById(id) {
try {
const product = await Product.findByPk(id, {
include: [{
model: Category,
as: "category",
attributes: ["id", "name"],
}],
});
if (!product) {
return { success: false, message: "Product not found" };
}
const data = product.toJSON();
return {
...data,
categoryName: data.category ? data.category.name : "Unknown",
};
} catch (error) {
console.error("Error getting product by ID:", error);
return { success: false, message: "Error reading product" };
}
}
async create(productData) {
try {
return await Product.create(productData);
} catch (error) {
console.error("Error creating product:", error);
return { success: false, message: "Error creating product", error: error.message };
}
}
async update(id, productData) {
try {
const product = await Product.findByPk(id);
if (!product) {
return { success: false, message: "Product not found" };
}
await product.update(productData);
return product;
} catch (error) {
console.error("Error updating product:", error);
return { success: false, message: "Error updating product" };
}
}
async delete(id) {
try {
const product = await Product.findByPk(id);
if (!product) {
return { success: false, message: "Product not found" };
}
const productData = product.toJSON();
await product.destroy();
return productData;
} catch (error) {
console.error("Error deleting product:", error);
return { success: false, message: "Error deleting product" };
}
}
}
module.exports = new ProductModel();Environment Variables
Configure your .env file:
DB_HOST=localhost
DB_PORT=3306
DB_NAME=inventory_management
DB_USER=root
DB_PASSWORD=your_mysql_password
NODE_ENV=developmentTesting the Connection
const database = require("./config/database");
async function startServer() {
const dbConnected = await database.testConnection();
if (!dbConnected) {
console.error("Failed to connect to database");
process.exit(1);
}
// Start your Express server
app.listen(3000, () => {
console.log("Server running on port 3000");
});
}
startServer();Best Practices
- Use singleton pattern to ensure only one database connection instance
- Configure connection pooling for better performance
- Always use environment variables for database credentials
- Implement proper error handling in model methods
- Use transactions for complex operations
- Set appropriate timeouts and retry logic
- Close connections gracefully on application shutdown
Conclusion
Sequelize ORM provides a powerful, type-safe interface for MySQL operations in Node.js. Using a singleton pattern ensures efficient connection management, while connection pooling improves performance. This setup is production-ready and scalable for inventory management systems and other data-heavy applications.