Express model qatlami va kod tuzilishi
Oldingi darsda marshrutlar bazaga ulandi va kod
ishladi. Lekin routes/tasks.js ni qayta o’qib chiqing: har marshrut
ichida database.prepare(...) va SQL matni turibdi. Beshta marshrutda
SELECT * FROM tasks WHERE id = ? uch marta takrorlangan.
Ustun nomi o’zgarsa, uni beshta joyda tuzatishingiz kerak bo’ladi.
Marshrutning ishi nima
Marshrut funksiyasi hozir uchta ish qilyapti:
- So’rovdan ma’lumot oladi (
request.body,request.params); - SQL yozadi va bazaga boradi;
- Javob va holat kodini qaytaradi.
Birinchi va uchinchisi — HTTP ishi. Ikkinchisi — ma’lumot ishi. Ular bir-biriga aloqasi yo’q ikki mavzu, lekin bitta funksiyada chalkashib yotibdi.
Ajratish nima beradi
Uchta aniq foyda bor.
| Muammo | Ajratilmagan kodda | Model qatlami bilan |
|---|---|---|
| SQL takrorlanadi | Beshta marshrutda | Bitta funksiyada |
| Test yozish | Server ishga tushirish kerak | Funksiyani to’g’ridan chaqirasiz |
| Bazani almashtirish | Har marshrut qayta yoziladi | Faqat model o’zgaradi |
Uchinchi band uzoq muddatli. SQLite’dan PostgreSQL’ga o’tsangiz,
models/task.js ichini qayta yozasiz — routes/tasks.js ga esa qo’l ham
tegmaydi, chunki u Task.getAll() ni chaqiradi va natijada massiv
kutadi. Bu shart o’zgarmaydi.
Papka tuzilishi
task-app/
├── db/
│ └── index.js ← ulanish
├── models/
│ └── task.js ← SQL shu yerda
├── routes/
│ └── tasks.js ← faqat HTTP
├── views/
├── public/
└── app.js
Qoidani bitta jumlaga sig’diring: routes/ ichida SELECT so’zi
uchramaydi, models/ ichida response so’zi uchramaydi.
models/task.js
// models/task.js
const database = require("../db");
// Bazadagi qatorni ilova obyektiga aylantiradi
function toTask(row) {
return {
id: row.id,
title: row.title,
isDone: row.is_done === 1,
createdAt: row.created_at,
};
}
function getAll() {
const rows = database.prepare("SELECT * FROM tasks ORDER BY id").all();
return rows.map(toTask);
}
function getById(id) {
const row = database.prepare("SELECT * FROM tasks WHERE id = ?").get(id);
return row ? toTask(row) : null;
}
function create(title) {
const result = database
.prepare("INSERT INTO tasks (title) VALUES (?)")
.run(title);
return getById(result.lastInsertRowid);
}
function update(id, changes) {
const task = getById(id);
if (!task) {
return null;
}
// Yuborilmagan maydon eski qiymatida qoladi
const title = changes.title === undefined ? task.title : changes.title;
const isDone = changes.isDone === undefined ? task.isDone : changes.isDone;
database
.prepare("UPDATE tasks SET title = ?, is_done = ? WHERE id = ?")
.run(title, isDone ? 1 : 0, id);
return getById(id);
}
function remove(id) {
const result = database.prepare("DELETE FROM tasks WHERE id = ?").run(id);
return result.changes > 0;
}
module.exports = { getAll, getById, create, update, remove };
Beshta funksiya — CRUD ning to’rt amali va getById. Nomlar HTTP
usullari bilan atalmagani muhim: getAll deyiladi, handleGet emas.
Model HTTP haqida hech narsa bilmaydi.
create va update oxirida getById chaqirilishiga e’tibor bering.
Nega: baza created_at ni o’zi to’ldiradi, id ni o’zi beradi. Faqat
INSERT qilib qo’ysangiz, bu qiymatlarni bilmaysiz. Qayta o’qish —
haqiqiy holatni qaytarishning ishonchli yo’li.
Qaytariladigan qiymat shartnomasi
Model qatlamining eng muhim qismi — nima qaytarishi. Marshrut shunga tayanadi.
| Funksiya | Topilganda | Topilmaganda |
|---|---|---|
getAll() |
Obyektlar massivi | Bo’sh massiv [] |
getById(id) |
Obyekt | null |
create(title) |
Yaratilgan obyekt | — |
update(id, changes) |
Yangilangan obyekt | null |
remove(id) |
true |
false |
routes/tasks.js — endi faqat HTTP
const express = require("express");
const Task = require("../models/task");
const router = express.Router();
router.get("/", (request, response) => {
response.json(Task.getAll());
});
router.get("/:id", (request, response) => {
const task = Task.getById(Number(request.params.id));
if (!task) {
return response.status(404).json({ error: "Vazifa topilmadi" });
}
response.json(task);
});
router.post("/", (request, response) => {
const title = request.body.title;
if (typeof title !== "string" || title.trim() === "") {
return response.status(400).json({ error: "title maydoni majburiy" });
}
const task = Task.create(title.trim());
response.status(201).json(task);
});
router.put("/:id", (request, response) => {
const task = Task.update(Number(request.params.id), request.body);
if (!task) {
return response.status(404).json({ error: "Vazifa topilmadi" });
}
response.json(task);
});
router.delete("/:id", (request, response) => {
const isRemoved = Task.remove(Number(request.params.id));
if (!isRemoved) {
return response.status(404).json({ error: "Vazifa topilmadi" });
}
response.sendStatus(204);
});
module.exports = router;
Fayl deyarli ikki barobar qisqardi. Har marshrut endi bir qarashda
o’qiladi: ma’lumotni ol, yo’q bo’lsa 404, bor bo’lsa qaytar.
Task nomi bosh harf bilan yozilgani — konvensiya: modul butun bir
mavzuni ifodalaydi. const Task = require("../models/task") yozuvi
Task.getAll() chaqiruvini o’qishli qiladi.
curl -X POST http://localhost:3000/api/tasks -H "Content-Type: application/json" -d '{"title":"Model qatlami"}'
{"id":4,"title":"Model qatlami","isDone":false,"createdAt":"2026-09-12 14:01:41"}
curl -X PUT http://localhost:3000/api/tasks/4 -H "Content-Type: application/json" -d '{"isDone":true}'
{"id":4,"title":"Model qatlami","isDone":true,"createdAt":"2026-09-12 14:01:41"}
API tashqaridan butunlay o’zgarmadi. Bu yaxshi belgi: refaktoring ichki tuzilishni o’zgartiradi, xulq-atvorni emas.
Modelni serversiz sinash
Ajratishning eng ko’rinarli foydasi shu yerda. model-check.js:
const Task = require("./models/task");
console.log("Bazadagi vazifalar:", Task.getAll().length);
const task = Task.create("Test ichidan");
console.log("Yaratildi:", task);
console.log("O'chirildi:", Task.remove(task.id));
console.log("Yo'q id:", Task.getById(99999));
node model-check.js
Bazadagi vazifalar: 3
Yaratildi: {
id: 5,
title: 'Test ichidan',
isDone: false,
createdAt: '2026-09-12 14:01:41'
}
O'chirildi: true
Yo'q id: null
Server ishga tushirilmadi, curl yozilmadi, port band qilinmadi. Shunchaki
funksiya chaqirildi. Testlash darsida bu yondashuv
avtomatik testlarga aylanadi.
Xato qayerda ushlanadi
Yangi savol tug’iladi: baza xato bersa (fayl o’chirilgan, disk to’lgan), uni kim ushlaydi?
Javob: model ushlamaydi. U xatoni yuqoriga qo’yib yuboradi, marshrut
esa next(error) bilan xato middleware’iga uzatadi.
router.get("/", (request, response, next) => {
try {
response.json(Task.getAll());
} catch (error) {
next(error);
}
});
Sabab: model javob qaytara olmaydi — uning response obyekti yo’q. Xatoni
yutib yuborsa (catch ichida console.log qilib qo’ysa), marshrut hech
narsa bo’lmagandek davom etadi va mijoz noto’g’ri javob oladi.
Qatlamlar qanchagacha ko’payadi
Katta loyihalarda oraliqda yana bir qatlam paydo bo’ladi.
| Qatlam | Vazifasi | Misol |
|---|---|---|
| Route (controller) | HTTP so’rov va javob | routes/tasks.js |
| Service | Biznes qoidalari | “Bajarilgan vazifani tahrirlab bo’lmaydi” |
| Model (repository) | Baza bilan muloqot | models/task.js |
Amaliy mashq
task-app loyihasida davom eting:
models/papkasini yaratib,task.jsichigagetAllvagetByIdfunksiyalarini ko’chiring;routes/tasks.jsdagiGETmarshrutlarini model chaqiruviga almashtiring vacurlbilan javob o’zgarmaganini tasdiqlang;create,update,removeni ham ko’chirib,routes/tasks.jsdadatabaseso’zi qolmaganini tekshiring;model-check.jsyozib, serverni ishga tushirmasdanTask.createvaTask.removeni sinang;getByIddanullo’rnigaundefinedqaytaring va marshrut hamon404berishini kuzating, so’ngnullga qaytaring;updatega faqat{ title: "Yangi nom" }yuboring —isDoneeski qiymatida qolishi kerak.
4-qadam ajratishning asosiy foydasini ko’rsatadi: model qatlami HTTP siz ham to’liq ishlaydi.
Qisqacha xulosa
- Marshrut ichidagi SQL takrorlanadi va o’zgartirishni qiyinlashtiradi.
routes/faqat HTTP bilan,models/faqat ma’lumot bilan ishlaydi.- Model funksiyalari HTTP tushunchalarini bilmaydi:
getAll,create,removedeb ataladi. - Qaytariladigan qiymat shartnomasi aniq bo’lsin: topilmasa
null, o’chirilsatrue. createvaupdateoxirida yozuvni qayta o’qishidvacreated_atkabi baza to’ldirgan maydonlarni qaytaradi.- Model xatoni ushlamaydi — marshrut uni
next(error)bilan uzatadi. - Ajratilgan modelni server ishga tushirmasdan sinash mumkin — bu testlashning asosi.
Keyingi darsda migratsiya va seed — jadval tuzilishini qo’lda emas, kod bilan boshqarish.