Express yakuniy loyiha: API va frontend
O’n dars davomida har tushunchani alohida ko’rdik: marshrut, middleware,
statik fayl, xato ishlovchisi, CRUD API va shablon. Endi ularni bitta
ishlaydigan ilovaga yig’amiz — server ham sahifani beradi, ham API bilan
xizmat qiladi, frontend esa fetch orqali u bilan gaplashadi.
Bo’limni jamlash
Loyihaga kirishdan oldin o’tilganni bir ko’z bilan ko’rib chiqamiz. Har qatordagi havola — o’sha tushuncha batafsil yozilgan dars.
| Tushuncha | Asosiy vosita | Dars |
|---|---|---|
| Framework g’oyasi | express() |
Express nima |
| Server ishga tushirish | app.listen() |
Birinchi server |
| Manzillar | app.get(), :id, Router |
Marshrutlash |
| Ma’lumot olish va berish | request.body, response.json() |
So’rov va javob |
| Oraliq funksiyalar | app.use(), next() |
Middleware |
| Fayl berish | express.static() |
Statik fayllar |
Xato va 404 |
To’rt parametrli middleware | Xatolarni boshqarish |
| To’rt amal | GET, POST, PUT, DELETE |
CRUD API |
| Ma’lumot tekshiruvi | Server tomonda validatsiya | Forma va validatsiya |
| HTML sahifa | response.render() |
EJS shabloni |
Loyiha nima qiladi
Vazifalar ilovasi. Foydalanuvchi brauzerda ro’yxatni ko’radi, yangi
vazifa qo’shadi, bajarilgan deb belgilaydi va o’chiradi. Sahifa
yangilanmaydi — hamma amal fetch bilan bajariladi.
Loyiha tuzilishi
task-app/
├── public/
│ ├── style.css
│ └── app.js
├── routes/
│ └── tasks.js
├── views/
│ ├── partials/
│ │ ├── header.ejs
│ │ └── footer.ejs
│ └── index.ejs
├── app.js
└── package.json
Har papkaning bitta vazifasi bor: public — brauzerga beriladigan
fayllar, routes — API marshrutlari, views — HTML shablonlar. Ildizdagi
app.js hammasini bog’laydi.
Boshlash:
mkdir task-app
cd task-app
npm init -y
npm install express ejs
npm install --save-dev nodemon
package.json ga ishga tushirish buyrug’ini qo’shing:
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
}
}
1-qadam: asosiy fayl
app.js:
const express = require("express");
const path = require("path");
const tasksRouter = require("./routes/tasks");
const app = express();
const PORT = 3000;
// Shablon sozlamalari
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
// Kirish middleware'lari — marshrutlardan yuqorida
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, "public")));
// Bosh sahifa — HTML qaytaradi
app.get("/", (request, response) => {
response.render("index", { pageTitle: "Vazifalar ilovasi" });
});
// API — JSON qaytaradi
app.use("/api/tasks", tasksRouter);
// Topilmagan manzil
app.use((request, response) => {
response.status(404).json({ error: "Bunday manzil topilmadi" });
});
// Xato ishlovchisi — to'rt parametr
app.use((err, request, response, next) => {
// Tafsilot faqat terminalga
console.error(err);
response.status(500).json({ error: "Serverda xato yuz berdi" });
});
app.listen(PORT, () => {
console.log(`Server ishga tushdi: http://localhost:${PORT}`);
});
Tartibga e’tibor bering: middleware → marshrutlar → 404 → xato
ishlovchisi. Xatolarni boshqarish darsida
ko’rganimizdek, bu tartib o’zgarsa tizim buziladi.
2-qadam: API marshrutlari
routes/tasks.js — CRUD API darsidagi kod, ustiga
validatsiya darsidagi tekshiruv
qo’shilgan:
const express = require("express");
const router = express.Router();
let tasks = [
{ id: 1, title: "Express o'rganish", isDone: true },
{ id: 2, title: "Yakuniy loyihani yozish", isDone: false },
];
let nextId = 3;
// Sarlavhani tekshiradigan middleware
function checkTitle(request, response, next) {
const title = request.body.title;
if (typeof title !== "string" || title.trim() === "") {
return response.status(400).json({
errors: [{ field: "title", message: "Sarlavha bo'sh bo'lmasin" }],
});
}
if (title.trim().length > 100) {
return response.status(400).json({
errors: [{ field: "title", message: "Sarlavha 100 belgidan oshmasin" }],
});
}
next();
}
// Hamma vazifa
router.get("/", (request, response) => {
response.json(tasks);
});
// Yangi vazifa
router.post("/", checkTitle, (request, response) => {
const task = {
id: nextId,
title: request.body.title.trim(),
isDone: false,
};
nextId = nextId + 1;
tasks.push(task);
response.status(201).json(task);
});
// Holatni almashtirish
router.put("/:id", (request, response) => {
const id = Number(request.params.id);
const task = tasks.find((item) => item.id === id);
if (!task) {
return response.status(404).json({ error: "Vazifa topilmadi" });
}
task.isDone = !task.isDone;
response.json(task);
});
// O'chirish
router.delete("/:id", (request, response) => {
const id = Number(request.params.id);
const exists = tasks.some((item) => item.id === id);
if (!exists) {
return response.status(404).json({ error: "Vazifa topilmadi" });
}
tasks = tasks.filter((item) => item.id !== id);
response.sendStatus(204);
});
module.exports = router;
Serverni ishga tushirib, API ni alohida tekshiring:
npm run dev
curl http://localhost:3000/api/tasks
[{"id":1,"title":"Express o'rganish","isDone":true},{"id":2,"title":"Yakuniy loyihani yozish","isDone":false}]
API ishlayotganiga ishonch hosil qilgandan keyingina frontendga o’ting. Ikkisini bir vaqtda yozsangiz, xato qayerdaligini aniqlash qiyinlashadi.
3-qadam: shablonlar
views/partials/header.ejs:
<!DOCTYPE html>
<html lang="uz">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= pageTitle %></title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<main>
views/partials/footer.ejs:
</main>
<script src="/app.js"></script>
</body>
</html>
views/index.ejs:
<%- include("partials/header") %>
<h1><%= pageTitle %></h1>
<form id="task-form">
<input
type="text"
id="task-input"
placeholder="Yangi vazifa"
maxlength="100"
/>
<button type="submit">Qo'shish</button>
</form>
<p id="message"></p>
<ul id="task-list"></ul>
<%- include("partials/footer") %>
Ro’yxat bo’sh: uni brauzerdagi JavaScript to’ldiradi. <script> tegi
footer’da, sahifa oxirida turibdi — shunda skript ishga tushganda
elementlar allaqachon mavjud bo’ladi.
4-qadam: frontend
public/app.js — brauzerda ishlaydigan kod:
const form = document.getElementById("task-form");
const input = document.getElementById("task-input");
const list = document.getElementById("task-list");
const message = document.getElementById("message");
// Serverdan ro'yxatni olib, sahifaga chizamiz
async function loadTasks() {
try {
const response = await fetch("/api/tasks");
if (!response.ok) {
throw new Error("Ro'yxat yuklanmadi");
}
const tasks = await response.json();
renderTasks(tasks);
} catch (error) {
message.textContent = error.message;
}
}
function renderTasks(tasks) {
// Eski ro'yxatni tozalaymiz
list.textContent = "";
for (const task of tasks) {
const item = document.createElement("li");
const titleSpan = document.createElement("span");
// textContent matnni matn sifatida qo'yadi — teg bajarilmaydi
titleSpan.textContent = task.title;
if (task.isDone) {
titleSpan.style.textDecoration = "line-through";
}
const toggleButton = document.createElement("button");
toggleButton.textContent = task.isDone ? "Qaytarish" : "Bajarildi";
toggleButton.addEventListener("click", () => toggleTask(task.id));
const deleteButton = document.createElement("button");
deleteButton.textContent = "O'chirish";
deleteButton.addEventListener("click", () => deleteTask(task.id));
item.append(titleSpan, toggleButton, deleteButton);
list.append(item);
}
}
form.addEventListener("submit", async (e) => {
e.preventDefault();
message.textContent = "";
try {
const response = await fetch("/api/tasks", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: input.value }),
});
const data = await response.json();
if (!response.ok) {
// Server qaytargan tekshiruv xatosini ko'rsatamiz
message.textContent = data.errors[0].message;
return;
}
input.value = "";
loadTasks();
} catch (error) {
message.textContent = "Server bilan bog'lanib bo'lmadi";
}
});
async function toggleTask(id) {
await fetch(`/api/tasks/${id}`, { method: "PUT" });
loadTasks();
}
async function deleteTask(id) {
await fetch(`/api/tasks/${id}`, { method: "DELETE" });
loadTasks();
}
loadTasks();
fetch va async/await fetch darsida va
async/await darsida ko’rilgan. Yangi jihat
bitta: so’rov endi o’zingiz yozgan serverga ketyapti.
5-qadam: stillar
public/style.css:
body {
font-family: system-ui, sans-serif;
background: #f9fafb;
margin: 0;
padding: 24px;
}
main {
max-width: 560px;
margin: 0 auto;
background: #ffffff;
padding: 24px;
border-radius: 12px;
}
h1 {
margin-top: 0;
color: #1f2937;
}
#task-form {
display: flex;
gap: 8px;
}
#task-input {
flex: 1;
padding: 8px;
border: 1px solid #d1d5db;
border-radius: 6px;
}
#message {
color: #b91c1c;
min-height: 20px;
}
#task-list {
list-style: none;
padding: 0;
}
#task-list li {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid #e5e7eb;
}
#task-list span {
flex: 1;
}
Ishlayotganini tekshirish
Brauzerda http://localhost:3000 ni oching va tartib bilan sinang:
- Ro’yxatda ikkita boshlang’ich vazifa ko’rinadimi;
- Yangi vazifa qo’shilib, ro’yxatda darhol paydo bo’ladimi;
- Bajarildi tugmasi matnni chizib tashlaydimi;
- O’chirish vazifani yo’q qiladimi;
- Bo’sh maydon bilan Qo’shish bosilsa, qizil xato xabari chiqadimi;
- Sahifa yangilanganda ro’yxat saqlanib qoladimi.
Oxirgi band eng muhimi: ma’lumot serverda turibdi, shuning uchun sahifani yangilash uni yo’qotmaydi.
Loyihani baholash mezoni
O’z ishingizni shu ro’yxat bo’yicha tekshiring:
| Mezon | Qanday tekshiriladi |
|---|---|
| To’rt amal ishlaydi | Qo’shish, ko’rish, belgilash, o’chirish |
| To’g’ri holat kodlari | curl -i da 201, 204, 400, 404 |
| Server tekshiruvi bor | curl bilan bo’sh title yuborib ko’ring |
| Xato foydalanuvchiga ko’rinadi | Bo’sh maydonda qizil xabar chiqadi |
| Tafsilot yashiringan | 500 javobida stack yo’q |
| Papkalar ajratilgan | routes, views, public alohida |
| Frontend matnni xavfsiz qo’yadi | innerHTML ishlatilmagan |
Oxirgi ikki band ko’pincha e’tibordan chetda qoladi, lekin haqiqiy loyihada eng ko’p muammo tug’diradigan joy aynan shular.
Bilimni tekshirish
Kodga qaramay javob berishga urinib ko’ring:
app.use(express.json())ni marshrutlardan pastga ko’chirsangiz nima buziladi?next()chaqirilmagan middleware qanday alomat beradi?- Nega
idni mijoz emas, server beradi? <%= %>va<%- %>orasidagi farq qaysi hujum turiga bog’liq?- Xato middleware’ini Express qaysi belgisiga qarab taniydi?
DELETEjavobida nega204qaytariladi,200emas?publicpapkasiga.envfaylini qo’ysangiz nima bo’ladi?
Javobi darhol esga kelmagan savol bo’lsa, yuqoridagi jadvaldan tegishli darsga qayting — bu bilimdagi bo’shliqni topishning eng tez yo’li.
Loyihani kengaytirish
Ilova ishlagach, o’zingizni sinash uchun qo’shimcha vazifalar:
- Vazifani tahrirlash imkonini qo’shing (
PUTbilantitleyangilash); - Bajarilganlarni yashiradigan filtr qiling (
?isDone=falsebilan); - Har vazifaga muhimlik darajasi va saralash qo’shing;
morganni ulab, so’rovlarni terminalda kuzating;/aboutsahifasiniincludeishlatib yarating.
Har biri o’tilgan darslardagi vositalar bilan bajariladi — yangi tushuncha kerak emas.
Nima o’rgandingiz va endi nima
Bu bo’limda sof http moduli bilan qo’lda yozilgan serverdan boshlab,
papkalarga bo’lingan, xatolarni boshqaradigan va frontend bilan
gaplashadigan ilovagacha yetdingiz. Marshrut, middleware, request va
response, statik fayl, shablon va CRUD — Node.js frameworklarining
deyarli hammasida shu tushunchalar bir xil ataladi.
Loyihada ataylab qoldirilgan uchta bo’shliq bor va ularning har biri keyingi qadam:
Ma’lumotlar bazasi. tasks massivi server qayta yuklanganda
tozalanadi. Haqiqiy loyihada ma’lumot bazada saqlanadi — bu mavzu bilan
ma’lumotlar bazasi darsida tanishib
chiqing, so’ng SQL bo’limiga o’ting.
Autentifikatsiya. Hozir ilovadagi vazifalar hamma uchun umumiy. Foydalanuvchini tanib, faqat o’z ro’yxatini ko’rsatish uchun kirish tizimi kerak: cookie, sessiya va autentifikatsiya darsi.
Deploy. Ilova hozir faqat localhost da ishlaydi. Uni internetga
chiqarish alohida ish:
deploy va operatsiyalar darsida
qaysi qadamlar kerakligi yozilgan.
Ilovani git bilan saqlab qo’ying va node_modules/ ni .gitignore ga
kiritishni unutmang — u haqda
npm va package.json darsida gapirilgan edi.
Bu loyiha portfelingizdagi birinchi backend ishi bo’lishi mumkin.