MongoDB — mongoose, schema, model, CRUD
MongoDB nima va PostgreSQL dan farqi?
PostgreSQL va MySQL — relyatsion ma'lumotlar bazasi. Ma'lumotlar jadvalda saqlanadi, qat'iy tuzilma bor, har ustunning turi oldindan belgilanadi.
MongoDB — hujjatli (document) ma'lumotlar bazasi. Ma'lumotlar JSON ga o'xshash hujjatlar sifatida saqlanadi. Qat'iy tuzilma yo'q — har hujjatda turli maydonlar bo'lishi mumkin.
Qachon MongoDB? Tuzilma tez-tez o'zgaradigan loyihalarda, ichma-ich ma'lumotlar ko'p bo'lganda (blog posti + izohlar + teglar — bitta hujjatda), prototip va MVP larda.
Qachon PostgreSQL? Murakkab so'rovlar, transaksiyalar, qat'iy ma'lumot yaxlitligi kerak bo'lganda.
Mongoose nima?
MongoDB ni Node.js dan to'g'ridan-to'g'ri ishlatish mumkin — rasmiy mongodb driver bor. Lekin u past darajali: schema yo'q, validatsiya yo'q, hamma narsa qo'lda.
Mongoose — MongoDB ustiga qurilgan kutubxona. U qo'shadi:
- Schema — hujjat qanday ko'rinishi kerakligini belgilash
- Validatsiya — ma'lumot to'g'riligini tekshirish
- Model — schema asosida CRUD operatsiyalari
Hayotiy o'xshatma: MongoDB — bo'sh qog'oz, istalgan narsani yozish mumkin. Mongoose — shakl (forma), faqat belgilangan joylarni to'ldirish mumkin.
npm install mongoose
Ulanish
// db.js
const mongoose = require('mongoose');
async function connectDB() {
try {
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/mydb');
console.log('MongoDB ga ulandi');
} catch (err) {
console.error('MongoDB ga ulanib bo\'lmadi:', err.message);
process.exit(1);
}
}
module.exports = connectDB;
// app.js
const connectDB = require('./db');
connectDB(); // server ishga tushganda ulanish
Schema — hujjat tuzilmasini belgilash
Schema — "bu kolleksiyada hujjatlar qanday ko'rinishi kerak" degan qoida. Maydonlar, turlari, validatsiya — hammasi shu yerda.
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Ism majburiy'],
trim: true, // bosh va oxiridagi bo'shliqlarni o'chirish
minlength: [2, 'Ism kamida 2 ta harf'],
},
email: {
type: String,
required: [true, 'Email majburiy'],
unique: true,
lowercase: true, // kichik harfga aylantirish
match: [/^\S+@\S+\.\S+$/, 'Email noto\'g\'ri formatda'],
},
age: {
type: Number,
min: [0, 'Yosh manfiy bo\'lmasligi kerak'],
max: [120, 'Yosh 120 dan oshmasligi kerak'],
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
},
{
timestamps: true, // createdAt va updatedAt avtomatik qo'shiladi
}
);
timestamps: true — juda qulay. Har hujjatga createdAt va updatedAt avtomatik qo'shiladi va yangilanadi.
Model — schema dan yaratiladi
Schema — qoida. Model — o'sha qoida asosida ma'lumotlar bazasi bilan ishlash uchun ob'ekt.
const User = mongoose.model('User', userSchema);
module.exports = User;
'User' — model nomi. Mongoose bu nomdan kolleksiya nomini hosil qiladi: users (kichik harf, ko'plik). Siz users kolleksiyasini qo'lda yaratmasangiz ham — Mongoose o'zi yaratadi.
CRUD
const express = require('express');
const router = express.Router();
const User = require('../models/User');
// CREATE
router.post('/', async (req, res, next) => {
try {
const user = await User.create(req.body);
res.status(201).json(user);
} catch (err) {
next(err);
}
});
// READ — hammasi
router.get('/', async (req, res, next) => {
try {
const users = await User.find().select('-__v'); // __v — versiya maydoni, kerak emas
res.json(users);
} catch (err) {
next(err);
}
});
// READ — bittasi
router.get('/:id', async (req, res, next) => {
try {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ message: 'Topilmadi' });
res.json(user);
} catch (err) {
next(err);
}
});
// UPDATE
router.patch('/:id', async (req, res, next) => {
try {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true } // new: yangilangan hujjatni qaytarish
);
if (!user) return res.status(404).json({ message: 'Topilmadi' });
res.json(user);
} catch (err) {
next(err);
}
});
// DELETE
router.delete('/:id', async (req, res, next) => {
try {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) return res.status(404).json({ message: 'Topilmadi' });
res.status(204).send();
} catch (err) {
next(err);
}
});
module.exports = router;
{ new: true } — findByIdAndUpdate standart bo'yicha eski hujjatni qaytaradi. new: true bilan — yangilangan hujjat qaytariladi.
runValidators: true — update da ham schema validatsiyasi ishlaydi. Standart bo'yicha update da validatsiya ishlamaydi — bu kutilmagan xatti-harakat.
Xato boshqarish — Mongoose xatolari
Mongoose o'ziga xos xatolar chiqaradi. Error middleware da ularni ushlab, tushunarli javob berish kerak:
app.use((err, req, res, next) => {
// Mongoose validatsiya xatosi
if (err.name === 'ValidationError') {
const messages = Object.values(err.errors).map(e => e.message);
return res.status(400).json({ message: messages.join(', ') });
}
// Noto'g'ri ID formati (ObjectId)
if (err.name === 'CastError') {
return res.status(400).json({ message: 'Noto\'g\'ri ID formati' });
}
// Unique constraint
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
return res.status(409).json({ message: `${field} allaqachon mavjud` });
}
console.error(err.stack);
res.status(500).json({ message: 'Server xatosi' });
});
Tez-tez uchraydigan xatolar
{ new: true } unutish:
const user = await User.findByIdAndUpdate(id, { name: 'Yangi' });
res.json(user); // eski ma'lumot qaytariladi!
runValidators: true unutish:
await User.findByIdAndUpdate(id, { age: -5 });
// Validatsiya ishlamaydi — -5 saqlanadi
Noto'g'ri ID bilan findById:
await User.findById('noto\'g\'ri-id');
// CastError: Cast to ObjectId failed
// Error middleware da ushlanishi kerak
Xulosa
MongoDB — hujjatli ma'lumotlar bazasi, qat'iy tuzilma yo'q. Mongoose — schema, validatsiya va model bilan ishlashni osonlashtiradi.
Schema — hujjat tuzilmasini belgilaydi. Model — schema asosida CRUD operatsiyalari. timestamps: true — createdAt va updatedAt avtomatik.
{ new: true, runValidators: true } — update da har doim qo'shiladi.
Keyingi dars
17-dars: Redis — ioredis, cache, TTL