Topic 012: ORMs & ODMs in Node.js
Object-Relational Mappers (ORMs) and ODM (Object-Document Mapping) in Node.js are tools that allow developers to interact with databases using an object-oriented paradigm. ORMs provide an abstraction layer over raw SQL queries, enabling developers to work with database data as if they were manipulating regular JavaScript objects. This can greatly simplify database operations and improve code maintainability.
ORM (Object-Relational Mapping) and ODM (Object-Document Mapping) are both techniques used to map application objects to database formats, but they serve different types of databases and have distinct characteristics. Here’s a detailed explanation of ORM and ODM, along with example code for each.
Definition: ORM is a programming technique used to convert data between incompatible type systems in object-oriented programming languages. It is used to interact with relational databases (SQL databases) by mapping objects in your code to database tables.
Examples of ORMs:
Key Features:
Example Using Sequelize (Node.js):
Install Sequelize and a SQL database driver (e.g., for PostgreSQL):
npm install sequelize pg pg-hstore
Define a Model and Perform Operations:
const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize("database", "username", "password", {
host: "localhost",
dialect: "postgres",
});
// Define a model
const User = sequelize.define("User", {
name: {
type: DataTypes.STRING,
allowNull: false,
},
age: {
type: DataTypes.INTEGER,
allowNull: false,
},
status: {
type: DataTypes.STRING,
},
score: {
type: DataTypes.INTEGER,
},
});
async function run() {
await sequelize.sync({ force: true }); // Sync all defined models to the DB
// Create a new user
const newUser = await User.create({ name: "John Doe", age: 30, status: "A", score: 90 });
// Find users with complex query
const users = await User.findAll({
where: {
age: { [Sequelize.Op.gt]: 25 },
[Sequelize.Op.or]: [{ status: "A" }, { score: { [Sequelize.Op.gt]: 80 } }],
},
});
console.log(users);
}
run().catch(console.error);
Definition: ODM is a technique used to map objects in object-oriented programming languages to documents in NoSQL databases (such as MongoDB). It allows the application to interact with the NoSQL database using objects, abstracting away the complexity of the database queries.
Examples of ODMs:
Key Features:
Example Using Mongoose (Node.js):
Install Mongoose:
npm install mongoose
Define a Model and Perform Operations:
const mongoose = require("mongoose");
const uri = "mongodb://localhost:27017/your_database";
mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
// Define a schema and model
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
status: String,
score: Number,
});
const User = mongoose.model("User", userSchema);
async function run() {
// Create a new user
const newUser = new User({ name: "John Doe", age: 30, status: "A", score: 90 });
await newUser.save();
// Find users with complex query
const users = await User.find({
age: { $gt: 25 },
$or: [{ status: "A" }, { score: { $gt: 80 } }],
}).exec();
console.log(users);
}
run()
.catch(console.error)
.finally(() => mongoose.connection.close());
Feature | ORM | ODM |
---|---|---|
Database Type | Relational (SQL) Databases | NoSQL Databases |
Data Structure | Tables with rows and columns | Documents (often JSON-like) |
Relationships | Uses foreign keys and joins | Uses embedded documents and references |
Schema | Fixed schema (enforced by the database) | Flexible schema (can be enforced by the ODM) |
Examples | Sequelize, Hibernate, Entity Framework | Mongoose, Morphia, Spring Data MongoDB |
Query Language | SQL | Query objects (e.g., MongoDB query language) |
Here’s a detailed look at three popular ORMs & ODMs in Node.js: Mongoose, Sequelize, and Knex.
Mongoose is an ODM (Object Data Modeling) library for MongoDB and Node.js. It provides a schema-based solution to model your application data.
npm install mongoose
const mongoose = require("mongoose");
mongoose
.connect("mongodb://localhost:27017/mydatabase", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to MongoDB");
})
.catch((error) => {
console.error("Error connecting to MongoDB:", error);
});
const { Schema } = mongoose;
const userSchema = new Schema({
name: String,
email: String,
age: Number,
});
const User = mongoose.model("User", userSchema);
const newUser = new User({
name: "John Doe",
email: "john@example.com",
age: 30,
});
newUser
.save()
.then(() => {
console.log("User saved");
})
.catch((error) => {
console.error("Error saving user:", error);
});
User.find({ age: { $gt: 25 } })
.then((users) => {
console.log("Users older than 25:", users);
})
.catch((error) => {
console.error("Error finding users:", error);
});
Sequelize is a promise-based ORM for Node.js, supporting many SQL dialects like MySQL, PostgreSQL, SQLite, and MSSQL.
npm install sequelize
npm install mysql2 # Or any other SQL dialect driver
const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize("database", "username", "password", {
host: "localhost",
dialect: "mysql", // Change this to your dialect
});
const User = sequelize.define(
"User",
{
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.INTEGER,
},
},
{
tableName: "users",
}
);
sequelize.sync().then(() => {
console.log("Database & tables created!");
});
User.create({
name: "Jane Doe",
email: "jane@example.com",
age: 28,
}).then((user) => {
console.log("User created:", user);
});
User.findAll({ where: { age: { [Sequelize.Op.gt]: 25 } } }).then((users) => {
console.log("Users older than 25:", users);
});
Knex.js is a SQL query builder for Node.js, supporting various SQL dialects. Unlike ORMs, Knex.js is primarily a query builder but can be used with other ORMs like Bookshelf.js for a more ORM-like experience.
npm install knex
npm install pg # Or any other SQL dialect driver
const knex = require("knex")({
client: "pg",
connection: {
host: "127.0.0.1",
user: "your_database_user",
password: "your_database_password",
database: "myapp_test",
},
});
knex.schema
.createTable("users", (table) => {
table.increments("id");
table.string("name");
table.string("email");
table.integer("age");
})
.then(() => {
console.log("Table created");
})
.catch((error) => {
console.error("Error creating table:", error);
});
knex("users")
.insert({
name: "Alice",
email: "alice@example.com",
age: 25,
})
.then(() => {
console.log("Data inserted");
});
knex("users")
.where("age", ">", 20)
.select("name", "email")
.then((users) => {
console.log("Users older than 20:", users);
});
knex.destroy().then(() => {
console.log("Connection closed");
});
Each of these tools has its own strengths and is suitable for different types of projects and database requirements.