Express bilan CRUD API yozish

Oldingi yetti darsda marshrut, request, response, middleware va xato ishlovchisini alohida-alohida ko’rdik. Endi ularni birlashtirib, to’liq ishlaydigan API yozamiz: vazifalarni qo’shish, ro’yxatini olish, o’zgartirish va o’chirish.

CRUD nima

Har amal o’z HTTP usuli va manziliga ega. Yozadigan beshta endpoint:

Usul Manzil Vazifa Muvaffaqiyat kodi
GET /api/tasks Hamma vazifa 200
GET /api/tasks/:id Bitta vazifa 200
POST /api/tasks Yangi qo’shish 201
PUT /api/tasks/:id Yangilash 200
DELETE /api/tasks/:id O’chirish 204

REST tamoyillari

Yuqoridagi jadval tasodifiy tuzilmagan. U REST uslubiga amal qiladi.

Farqni taqqoslashda ko’rish oson:

REST uslubida REST’ga zid
GET /api/tasks GET /api/getAllTasks
POST /api/tasks GET /api/createTask
DELETE /api/tasks/5 GET /api/deleteTask?id=5

O’ng ustunda amal manzilga yozilgan va hammasi GET bilan bajarilyapti. Bu xavfli ham: qidiruv roboti havolani ochsa, ma’lumot o’chib ketishi mumkin. GET faqat o’qiydi, o’zgartirmaydi. Batafsil REST, GraphQL va WebSocket darsida.

Ma’lumotni qayerda saqlaymiz

Ma’lumotlar bazasi hali o’tilmagan, shuning uchun vazifalarni oddiy massivda saqlaymiz.

// Vaqtinchalik ombor — server xotirasida
let tasks = [
  { id: 1, title: "Kitob o'qish", isDone: false },
  { id: 2, title: "Kod yozish", isDone: true },
];

let nextId = 3;

let ishlatilgani bejiz emas: DELETE da massivni filter natijasi bilan almashtiramiz.

Tayyorgarlik

routes/tasks.js faylini ochamiz — marshrutlash darsida yaratgan Router shu yerda kengayadi.

// routes/tasks.js
const express = require("express");

const router = express.Router();

let tasks = [
  { id: 1, title: "Kitob o'qish", isDone: false },
  { id: 2, title: "Kod yozish", isDone: true },
];

let nextId = 3;

module.exports = router;

app.js esa shunday:

// app.js
const express = require("express");
const tasksRouter = require("./routes/tasks");

const app = express();

// So'rov tanasidagi JSON ni o'qish uchun
app.use(express.json());

app.use("/api/tasks", tasksRouter);

app.use((request, response) => {
  response.status(404).json({ error: "Bunday manzil topilmadi" });
});

app.use((err, request, response, next) => {
  console.error(err);
  response.status(500).json({ error: "Serverda xato yuz berdi" });
});

app.listen(3000, () => {
  console.log("Server ishga tushdi: http://localhost:3000");
});

Quyidagi beshta endpoint module.exports qatoridan yuqoriga yoziladi.

1. GET — hamma vazifa

router.get("/", (request, response) => {
  response.json(tasks);
});
curl http://localhost:3000/api/tasks
[{"id":1,"title":"Kitob o'qish","isDone":false},{"id":2,"title":"Kod yozish","isDone":true}]

Eng sodda endpoint: massivni JSON qilib qaytaradi.

2. GET — bitta vazifa

router.get("/: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" });
  }

  response.json(task);
});
curl http://localhost:3000/api/tasks/1
{"id":1,"title":"Kitob o'qish","isDone":false}

Mavjud bo’lmagan raqam:

curl -i http://localhost:3000/api/tasks/99
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8

{"error":"Vazifa topilmadi"}

request.params.id matn bo’lgani uchun Number() bilan aylantirildi — usiz item.id === id taqqoslash hech qachon true bermaydi.

3. POST — yangi vazifa

router.post("/", (request, response) => {
  const title = request.body.title;

  // Kelgan ma'lumotni ishonchsiz deb qaraymiz
  if (!title || title.trim() === "") {
    return response.status(400).json({ error: "title maydoni majburiy" });
  }

  const task = {
    id: nextId,
    title: title.trim(),
    isDone: false,
  };

  nextId = nextId + 1;
  tasks.push(task);

  // 201 — yangi resurs yaratildi
  response.status(201).json(task);
});
curl -i -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"Dars tayyorlash"}'
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{"id":3,"title":"Dars tayyorlash","isDone":false}

Uch jihatga e’tibor bering. id ni server beradi — mijoz yuborgan id ni qabul qilmaymiz, aks holda ikki vazifa bir xil raqam olishi mumkin. isDone boshlang’ich qiymati false. Javobda yaratilgan obyekt qaytariladi — mijoz id ni shu yerdan biladi.

Bo’sh sarlavha bilan sinang:

curl -i -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"   "}'
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{"error":"title maydoni majburiy"}

4. PUT — yangilash

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" });
  }

  const title = request.body.title;
  const isDone = request.body.isDone;

  // Faqat yuborilgan maydonlarni yangilaymiz
  if (title !== undefined) {
    if (title.trim() === "") {
      return response.status(400).json({ error: "title bo'sh bo'lmasin" });
    }
    task.title = title.trim();
  }

  if (isDone !== undefined) {
    task.isDone = Boolean(isDone);
  }

  response.json(task);
});
curl -X PUT http://localhost:3000/api/tasks/1 \
  -H "Content-Type: application/json" \
  -d '{"isDone":true}'
{"id":1,"title":"Kitob o'qish","isDone":true}

find massivdagi obyektga havola qaytaradi, shuning uchun task.isDone = true massivdagi qiymatni ham o’zgartiradi. id esa o’zgarmaydi — u resursning o’zgarmas belgisi.

5. DELETE — 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);

  // 204 — muvaffaqiyatli, qaytaradigan ma'lumot yo'q
  response.sendStatus(204);
});
curl -i -X DELETE http://localhost:3000/api/tasks/2
HTTP/1.1 204 No Content

filter yangi massiv qaytaradi va uni tasks ga yozamiz — let shuning uchun kerak edi. 204 javobida tana bo’lmaydi, shuning uchun response.json() emas, response.sendStatus() ishlatiladi.

To’liq fayl

routes/tasks.js yakuniy ko’rinishi:

const express = require("express");

const router = express.Router();

let tasks = [
  { id: 1, title: "Kitob o'qish", isDone: false },
  { id: 2, title: "Kod yozish", isDone: true },
];

let nextId = 3;

router.get("/", (request, response) => {
  response.json(tasks);
});

router.get("/: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" });
  }

  response.json(task);
});

router.post("/", (request, response) => {
  const title = request.body.title;

  if (!title || title.trim() === "") {
    return response.status(400).json({ error: "title maydoni majburiy" });
  }

  const task = { id: nextId, title: title.trim(), isDone: false };

  nextId = nextId + 1;
  tasks.push(task);

  response.status(201).json(task);
});

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" });
  }

  const title = request.body.title;
  const isDone = request.body.isDone;

  if (title !== undefined) {
    if (title.trim() === "") {
      return response.status(400).json({ error: "title bo'sh bo'lmasin" });
    }
    task.title = title.trim();
  }

  if (isDone !== undefined) {
    task.isDone = Boolean(isDone);
  }

  response.json(task);
});

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;

To’liq sinov ketma-ketligi

Serverni npm run dev bilan ishga tushiring va quyidagilarni tartib bilan bajaring:

curl http://localhost:3000/api/tasks
curl -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"Yangi vazifa"}'
curl -X PUT http://localhost:3000/api/tasks/3 \
  -H "Content-Type: application/json" \
  -d '{"isDone":true}'
curl -X DELETE http://localhost:3000/api/tasks/3
curl http://localhost:3000/api/tasks

Oxirgi buyruq boshlang’ich ikki vazifani ko’rsatadi: qo’shilgan yozuv yangilandi, so’ng o’chirildi. To’rt amal ham ishladi.

Amaliy mashq

express-app loyihasida davom eting:

  1. routes/tasks.js da tasks massivi va nextId ni e’lon qilib, GET / endpointini yozing;
  2. GET /:id ni qo’shib, mavjud va mavjud bo’lmagan raqam bilan tekshiring;
  3. POST / ni yozing va javob kodi 201 ekanini curl -i bilan tasdiqlang;
  4. Bo’sh title yuborib, 400 javobini oling;
  5. PUT /:id bilan vazifani bajarilgan deb belgilang, so’ng GET /api/tasks da o’zgarish saqlanganini ko’ring;
  6. DELETE /:id ni yozib, o’chirilgan vazifani qayta so’rang — 404 chiqishi kerak.

6-qadam butun zanjirni tekshiradi: o’chirish ishlagan bo’lsa, keyingi so’rov aynan 404 qaytaradi.

Qisqacha xulosa

  • CRUD — yaratish, o’qish, yangilash va o’chirish; har biri o’z HTTP usuliga mos keladi.
  • REST’da manzil resursni, usul esa amalni bildiradi: DELETE /api/tasks/5, GET /api/deleteTask emas.
  • id ni server beradi, mijozdan qabul qilinmaydi.
  • To’g’ri holat kodlari: yaratishda 201, topilmasa 404, noto’g’ri ma’lumotda 400, o’chirishda 204.
  • Har endpointda kelgan ma’lumot tekshiriladi — bo’sh title bazaga tushmasligi kerak.
  • find obyektga havola beradi, filter esa yangi massiv — shuning uchun tasks let bilan e’lon qilinadi.
  • Xotiradagi massiv server qayta yuklanganda tozalanadi.

Keyingi darsda forma va validatsiya — HTML formadan kelgan ma’lumotni qabul qilish va uni jiddiy tekshirish.

Bu dars foydali bo'ldimi?

marta ko'rildi