Express yakuniy loyiha: to'liq ilova
O’n ikki dars oldin task-app ilovasidagi vazifalar oddiy massivda
yotardi va server o’chganda yo’qolardi. Hozir sizda baza, model qatlami,
migratsiya tizimi, kirish tizimi, fayl yuklash, xavfsizlik qatlamlari
va testlar bor.
Bu dars yangi tushuncha o’rgatmaydi. U o’tilganni bir joyga yig’adi va sizga tekshirib ko’radigan to’liq loyiha beradi.
Bo’limni jamlash
Har qatordagi havola — o’sha tushuncha batafsil yozilgan dars.
| Tushuncha | Asosiy vosita | Dars |
|---|---|---|
| Doimiy saqlash | node:sqlite, prepare |
Bazani ulash |
| Kod tuzilishi | models/, routes/ |
Model qatlami |
| Sxemani boshqarish | migrations/, migrate.js |
Migratsiya va seed |
| Serverning xotirasi | express-session, cookie |
Sessiya va cookie |
| Hisob yaratish | bcrypt.hash |
Ro’yxatdan o’tish |
| Kirish va himoya | bcrypt.compare, requireAuth |
Kirish va himoya |
| Tokenli API | jsonwebtoken, Bearer |
JWT va API token |
| Fayl qabul qilish | multer, multipart/form-data |
Fayl yuklash |
| Hujumlardan himoya | helmet, CSRF, rate limit |
Web xavfsizligi |
| Avtomatik sinash | node:test, supertest |
API testlash |
| Serverga chiqarish | NODE_ENV, PM2, Nginx |
Deploy |
Loyiha nima qiladi
Shaxsiy vazifalar ilovasi. Har foydalanuvchi ro’yxatdan o’tadi, tizimga kiradi va faqat o’z vazifalarini ko’radi. Profil rasmini yuklaydi. API esa mobil ilova uchun token bilan ham ishlaydi.
Oldingi yakuniy loyihadan farqi shu jumlada: “faqat o’z vazifalarini”. O’sha ilovada ma’lumot hamma uchun umumiy edi va server o’chganda yo’qolardi.
Loyiha tuzilishi
task-app/
├── db/
│ └── index.js ← ulanish
├── migrations/
│ ├── 001-create-tasks.js
│ ├── 002-add-priority.js
│ ├── 003-create-users.js
│ ├── 004-add-task-user.js
│ └── 005-add-user-avatar.js
├── models/
│ ├── task.js
│ └── user.js
├── middleware/
│ ├── auth.js ← requireAuth
│ └── csrf.js
├── routes/
│ ├── tasks.js ← HTML sahifalar
│ ├── auth.js ← kirish, chiqish
│ └── api.js ← JSON + JWT
├── views/
├── public/
├── uploads/
├── test/
│ ├── tasks.test.js
│ └── auth.test.js
├── app.js ← ilova
├── server.js ← listen
├── migrate.js
├── seed.js
├── .env
├── .env.example
└── .gitignore
Har papka bitta vazifaga javob beradi. Bu tuzilish
model qatlami darsida qo’yilgan qoidaning
o’sishi: routes/ ichida SELECT yo’q, models/ ichida response
yo’q.
Qadamlar ketma-ketligi
Loyihani noldan yig’sangiz, shu tartibda boring. Har qadam oldingisiga tayanadi.
1-qadam: asos. npm init, express@4, ejs o’rnatish. app.js va
server.js ni ajratib yozish — testlash darsida
ko’rilganidek.
2-qadam: baza va migratsiyalar. db/index.js, migrate.js va
beshta migratsiya fayli. npm run migrate bilan tekshiring.
3-qadam: model qatlami. models/task.js va models/user.js.
Serverni ishga tushirmasdan node -e bilan sinang.
4-qadam: kirish tizimi. Sessiya, bcrypt, routes/auth.js,
requireAuth.
5-qadam: vazifalar foydalanuvchiga bog’lanadi. Model funksiyalari
userId qabul qiladigan bo’ladi.
6-qadam: fayl yuklash. multer, uploads/, avatar marshruti.
7-qadam: xavfsizlik. helmet, CSRF tokeni, rate limiting, .env.
8-qadam: testlar. Kamida oltita test: CRUD, validatsiya, himoya.
9-qadam: deploy tayyorligi. PORT, trust proxy, /health,
.gitignore.
Egalik tekshiruvi — eng muhim qism
Beshinchi qadamda eng ko’p xato qilinadi. Model so’roviga user_id
sharti har funksiyaga qo’shiladi:
// models/task.js
function getAllByUser(userId) {
const rows = database
.prepare("SELECT * FROM tasks WHERE user_id = ? ORDER BY id")
.all(userId);
return rows.map(toTask);
}
function getByIdForUser(id, userId) {
const row = database
.prepare("SELECT * FROM tasks WHERE id = ? AND user_id = ?")
.get(id, userId);
return row ? toTask(row) : null;
}
function removeForUser(id, userId) {
const result = database
.prepare("DELETE FROM tasks WHERE id = ? AND user_id = ?")
.run(id, userId);
return result.changes > 0;
}
Ikki xil kirish: sessiya va token
Loyihada ikkala yondashuv ham bor va ular aralashmaydi.
| Marshrut guruhi | Kim uchun | Himoya |
|---|---|---|
/tasks, /login (HTML) |
Brauzer | Sessiya + CSRF |
/api/* (JSON) |
Mobil ilova, SPA | Authorization: Bearer |
Ikkalasi bitta model qatlamiga murojaat qiladi. Farq faqat kirish usulida va javob formatida:
// routes/tasks.js — brauzer uchun
router.get("/", requireAuthPage, (request, response) => {
const tasks = Task.getAllByUser(request.session.userId);
response.render("tasks", { pageTitle: "Mening vazifalarim", tasks: tasks });
});
// routes/api.js — mobil ilova uchun
router.get("/tasks", requireToken, (request, response) => {
response.json(Task.getAllByUser(request.userId));
});
Model funksiyasi bitta — bu ajratishning to’g’ridan-to’g’ri foydasi.
Yakuniy loyiha topshirig’i
Loyihani quyidagi talablar bo’yicha tugating.
Majburiy:
- Foydalanuvchi ro’yxatdan o’tadi va kiradi; parol
bcryptbilan hash qilinadi; - Har foydalanuvchi faqat o’z vazifalarini ko’radi, tahrirlaydi va o’chiradi;
- Baza tuzilishi migratsiyalar bilan boshqariladi;
- SQL faqat
models/ichida yoziladi; - Formalar CSRF tokeni bilan himoyalanadi, chiqish
POSTbilan bajariladi; - Kirish marshrutida urinishlar soni cheklangan;
- Profil rasmi yuklanadi: hajm va tur tekshiriladi, nom tasodifiy;
- Kamida oltita test yozilgan va
npm testo’tadi; - Sirlar
.envda,.envesa.gitignoreda.
Qo’shimcha (xohishga ko’ra):
/api/*marshrutlari JWT bilan ishlaydi;- Vazifalarni muhimlik bo’yicha saralash va filtrlash;
- Parolni almashtirish sahifasi (eski parol so’raladi);
/healthmarshruti vamorganlogi;- Sahifalash (
?page=2) bilan uzun ro’yxat.
Baholash mezoni
O’z ishingizni shu jadval bo’yicha tekshiring. O’ng ustun — qanday sinash kerakligi.
| Mezon | Qanday tekshiriladi |
|---|---|
| Ma’lumot saqlanadi | Serverni o’chirib yoqing, ro’yxat joyida qolsin |
| Parol ochiq emas | Bazaga qarang: $2b$10$ bilan boshlansin |
| Kirmagan kira olmaydi | curl bilan /api/tasks — 401 kelsin |
| Xato xabari umumiy | Yo’q email va xato parol bir xil javob bersin |
| SQL marshrutda yo’q | routes/ da SELECT so’zini qidiring — topilmasin |
| Migratsiya takrorlanmaydi | npm run migrate ni ikki marta yuriting |
| XSS to’silgan | <b>test</b> sarlavhali vazifa qo’shing — qalin chiqmasin |
| Fayl turi tekshiriladi | .txt fayl yuklang — 400 kelsin |
| Chegaradan katta fayl | 5 MB fayl yuklang — 413 kelsin |
| Rate limit ishlaydi | Kirishga olti marta urinib ko’ring — 429 kelsin |
| Testlar o’tadi | npm test — hamma ✔ bo’lsin |
| Egalik tekshiriladi | Ikki hisob oching; birinchisining vazifa id sini ikkinchisidan so’rang — 404 kelsin |
Oxirgi qator qalin yozilgani bejiz emas. Bu sinov haqiqiy loyihalarda eng ko’p uchraydigan xavfsizlik kamchiligini ochadi va uni qo’lda tekshirmasangiz, hech qachon sezmaysiz.
Bilimni tekshirish
Kodga qaramay javob berishga urinib ko’ring. Har savol yonida — javob yozilgan dars.
- Xotiradagi massiv va bazadagi jadval orasidagi asosiy farq nima? (Bazani ulash)
- SQL ga qiymatni matn birikmasi bilan qo’shish nega xavfli? (Bazani ulash)
routes/vamodels/orasidagi chegara qanday qoida bilan belgilanadi? (Model qatlami)- Bajarilgan migratsiya faylini nega tahrirlab bo’lmaydi? (Migratsiya)
- Cookie’ga
userIdyozish nega xato? (Sessiya va cookie) httpOnlybayrog’i qaysi hujumni to’sadi? (Sessiya va cookie)- Bir xil paroldan har safar boshqa hash chiqsa,
compareqanday ishlaydi? (Ro’yxatdan o’tish) - Kirish xatosida nega “email topilmadi” deb yozilmaydi? (Kirish va himoya)
401va403qachon ishlatiladi? (Kirish va himoya)- JWT payload’iga parol solish nega xato? (JWT va API token)
- Yuklangan faylga nega foydalanuvchi bergan nom berilmaydi? (Fayl yuklash)
<%= %>va<%- %>farqi qaysi hujumga bog’liq? (Xavfsizlik)request.agent(app)oddiyrequest(app)dan nimasi bilan farq qiladi? (Testlash)trust proxyyoqilmasa nima jimgina buziladi? (Deploy)
Javobi darhol esga kelmagan savol bo’lsa, o’sha darsga qayting — bu bilimdagi bo’shliqni topishning eng tez yo’li.
Xatolar ro’yxati
Bu bo’limda talabalar ko’p takrorlaydigan xatolar. Loyihangizni topshirishdan oldin har birini tekshiring.
| Xato | Alomati |
|---|---|
session() requireAuth dan keyin ulangan |
Cannot read properties of undefined (reading 'userId') |
await bcrypt.hash da await yo’q |
Bazada [object Promise], hech kim kira olmaydi |
enctype yozilmagan |
request.file undefined |
.get() natijasi tekshirilmagan |
Cannot read properties of undefined |
async marshrutda try/catch yo’q |
So’rov osilib qoladi, terminalda UnhandledPromiseRejection |
secure: true localhost da |
Sessiya umuman ishlamaydi |
Migratsiya pm2 reload dan keyin |
Yangi kod eski bazaga murojaat qiladi |
Model so’rovida user_id sharti yo’q |
Foydalanuvchi o’zganing ma’lumotini ko’radi |
Loyihani kengaytirish
Ilova ishlagach, keyingi qadamlar uchun g’oyalar:
- Vazifa bo’limlari —
categoriesjadvali va bog’lanish; - Birgalikda ishlash — vazifani boshqa foydalanuvchiga ulashish;
- Email tasdiqlash — ro’yxatdan o’tgandan keyin havola yuborish;
- Sahifalash va qidiruv —
?search=kitob&page=2; - PostgreSQL’ga o’tish — faqat
db/vamodels/o’zgaradi; - TypeScript — Node.js va TypeScript darsi bilan.
Beshinchi band ayniqsa qiziq: agar model qatlami to’g’ri ajratilgan bo’lsa, marshrutlarga umuman qo’l tegmaydi. Bu ajratishning haqiqiy sinovi.
Yo’l qayerga olib keldi
Bo’lim boshida express() funksiyasi va bitta app.get() bor edi. Hozir
sizda foydalanuvchi hisoblari, hash qilingan parollar, migratsiyali baza,
fayl yuklash, xavfsizlik qatlamlari, avtomatik testlar va serverga
chiqarish rejasi bor ilova turibdi.
Bu ro’yxatdagi hech narsa Express’ga xos emas. Model qatlami, migratsiya, sessiya, token, CSRF, rate limiting — bular hamma backend frameworkida bir xil ataladi va bir xil ishlaydi. Nest.js, Fastify yoki boshqa tilga o’tsangiz, sintaksis o’zgaradi, tushunchalar esa qoladi.
Loyihani git bilan saqlang va README.md yozing: ilova nima qiladi,
qanday ishga tushiriladi, qaysi muhit o’zgaruvchilari kerak. Bu fayl
loyihani ish beruvchiga ko’rsatganingizda birinchi o’qiladigan narsa
bo’ladi — va u sizning kodingiz haqida kod yozgan narsangizdan kam
gapirmaydi.