Node.js
ORM / Mongoose – Sequelize, Prisma & Mongoose
Model your data with an ORM or ODM: define Sequelize models and associations, write Prisma schemas and typed queries, and build Mongoose schemas with full CRUD operations.
Sequelize Model: Define a model with
DataTypes and let Sequelize generate SQL DDL. Use sync({ alter: true }) in dev to auto-migrate.src/models/User.js (Sequelize)
JS
const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize');
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: { isEmail: true },
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
role: {
type: DataTypes.ENUM('user', 'admin'),
defaultValue: 'user',
},
}, {
tableName: 'users',
timestamps: true, // adds createdAt, updatedAt
paranoid: true, // adds deletedAt (soft delete)
});
module.exports = User;
Sequelize Associations: Define
hasMany / belongsTo in an index.js that imports all models, so associations run once on startup.src/models/index.js
JS
const User = require('./User');
const Post = require('./Post');
const Comment = require('./Comment');
// User ──< Post
User.hasMany(Post, { foreignKey: 'userId', as: 'posts' });
Post.belongsTo(User, { foreignKey: 'userId', as: 'author' });
// Post ──< Comment
Post.hasMany(Comment, { foreignKey: 'postId', as: 'comments' });
Comment.belongsTo(Post, { foreignKey: 'postId' });
// User ──< Comment
User.hasMany(Comment, { foreignKey: 'userId' });
Comment.belongsTo(User, { foreignKey: 'userId', as: 'commenter' });
module.exports = { User, Post, Comment };
Prisma Schema: Define models in
schema.prisma. Run npx prisma migrate dev to apply migrations and npx prisma generate to regenerate the client.prisma/schema.prisma
PRISMA
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
body String @db.Text
published Boolean @default(false)
author User @relation(fields: [userId], references: [id])
userId Int
createdAt DateTime @default(now())
}
enum Role {
USER
ADMIN
}
Prisma Queries: The generated client provides fully-typed, auto-completed methods. Use
include to eager-load relations.src/services/postService.js (Prisma)
JS
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// Find all published posts with author
const getPublishedPosts = () =>
prisma.post.findMany({
where: { published: true },
include: { author: { select: { id: true, name: true } } },
orderBy: { createdAt: 'desc' },
});
// Create a post
const createPost = (userId, data) =>
prisma.post.create({
data: { ...data, userId },
});
// Update
const publishPost = (id) =>
prisma.post.update({
where: { id },
data: { published: true },
});
// Soft-delete (Prisma doesn't have built-in; use a flag)
const deletePost = (id) =>
prisma.post.delete({ where: { id } });
module.exports = { getPublishedPosts, createPost, publishPost, deletePost };
Mongoose Schema: Define schema shape, add virtual fields, pre-save hooks, and export the compiled
Model.src/models/User.js (Mongoose)
JS
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true, minlength: 6 },
role: { type: String, enum: ['user', 'admin'], default: 'user' },
avatar: { type: String, default: null },
}, {
timestamps: true, // adds createdAt, updatedAt
});
// Pre-save hook: hash password
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 12);
next();
});
// Instance method: compare password
userSchema.methods.comparePassword = function (plain) {
return bcrypt.compare(plain, this.password);
};
// Virtual: full profile URL
userSchema.virtual('profileUrl').get(function () {
return `/users/${this._id}`;
});
module.exports = mongoose.model('User', userSchema);
CRUD with Mongoose: Use
.lean() for read-only queries to skip Mongoose document overhead, and { new: true } to return the updated document.src/services/userService.js (Mongoose)
JS
const User = require('../models/User');
const getAll = () => User.find().select('-password').lean();
const getById = (id) => User.findById(id).select('-password').lean();
const getByEmail = (email)=> User.findOne({ email }).lean();
const create = (data) => User.create(data);
const update = (id, data) =>
User.findByIdAndUpdate(id, data, { new: true, runValidators: true })
.select('-password');
const remove = (id) => User.findByIdAndDelete(id);
// Paginated list
const paginate = async (page = 1, limit = 10) => {
const skip = (page - 1) * limit;
const [data, total] = await Promise.all([
User.find().skip(skip).limit(limit).select('-password').lean(),
User.countDocuments(),
]);
return { data, total, page, pages: Math.ceil(total / limit) };
};
module.exports = { getAll, getById, getByEmail, create, update, remove, paginate };