REST API — JSON qaytaruvchi oddiy API
Muammodan boshlaylik
Todo ilovangiz yaxshi ishlayapti. Endi:
- Mobil dasturchi Flutter ilova yasamoqchi
- Frontend dasturchi React bilan yangi interfeys yozmoqchi
- Telegram bot vazifalarni eslatib turishi kerak
Ularning hammasiga HTML kerak emas. Ularga ma'lumot kerak — JSON ko'rinishida, va uni qanday so'rashni aniq biladigan qoidalar. Bu qoidalar to'plami — API. Eng keng tarqalgan uslubi — REST.
REST qoidalari
REST da hamma narsa resurs (vazifa, foydalanuvchi, buyurtma) va har bir resurs bilan HTTP metodlari orqali ishlanadi:
| Metod | URL | Ma'nosi | Javob kodi |
|---|---|---|---|
GET |
/api/vazifalar |
Ro'yxat | 200 |
GET |
/api/vazifalar/5 |
Bittasini olish | 200 / 404 |
POST |
/api/vazifalar |
Yangi yaratish | 201 |
PUT / PATCH |
/api/vazifalar/5 |
O'zgartirish | 200 |
DELETE |
/api/vazifalar/5 |
O'chirish | 204 |
Qoidalar:
- URL — ot (
/vazifalar), fe'l emas (/vazifaniOchir❌). Amal — metod orqali - Ko'plikda:
/vazifalar,/vazifalar/5 - Natija — status kod orqali bildiriladi,
200ichida{"xato": true}emas
PUT — resursni butunlay almashtirish, PATCH — faqat berilgan maydonlarni o'zgartirish. Amalda ko'pincha PATCH qulayroq.
API yordamchilari
src/Api.php:
<?php
declare(strict_types=1);
namespace App;
final class Api
{
public static function javob(mixed $data, int $status = 200): never
{
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
if ($status !== 204) {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}
exit;
}
public static function xato(string $xabar, int $status, array $tafsilot = []): never
{
self::javob(['xato' => $xabar] + ($tafsilot ? ['tafsilot' => $tafsilot] : []), $status);
}
public static function tana(): array
{
$xom = file_get_contents('php://input');
if ($xom === '' || $xom === false) {
return [];
}
try {
$data = json_decode($xom, true, flags: JSON_THROW_ON_ERROR);
} catch (\JsonException) {
self::xato("JSON noto'g'ri", 400);
}
return is_array($data) ? $data : self::xato("JSON ob'ekt kutilgan", 400);
}
}
API controller
src/Controller/Api/VazifaApiController.php:
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Api;
use App\Model\VazifaRepository;
final class VazifaApiController
{
private VazifaRepository $repo;
public function __construct()
{
$this->repo = new VazifaRepository();
}
// GET /api/vazifalar
public function royxat(): void
{
$vazifalar = array_map([$this, 'formatla'], $this->repo->hammasi());
Api::javob(['data' => $vazifalar]);
}
// GET /api/vazifalar/{id}
public function bitta(string $id): void
{
$v = $this->repo->top((int) $id) ?? Api::xato('Vazifa topilmadi', 404);
Api::javob(['data' => $this->formatla($v)]);
}
// POST /api/vazifalar
public function yaratish(): void
{
$tana = Api::tana();
$matn = trim((string) ($tana['matn'] ?? ''));
if ($xatolar = VazifaRepository::tekshir($matn)) {
Api::xato('Validatsiya xatosi', 422, ['matn' => $xatolar]);
}
$id = $this->repo->qosh($matn);
header("Location: /api/vazifalar/$id");
Api::javob(['data' => $this->formatla($this->repo->top($id))], 201);
}
// PATCH /api/vazifalar/{id}
public function yangilash(string $id): void
{
$v = $this->repo->top((int) $id) ?? Api::xato('Vazifa topilmadi', 404);
$tana = Api::tana();
if (array_key_exists('matn', $tana)) {
$matn = trim((string) $tana['matn']);
if ($xatolar = VazifaRepository::tekshir($matn)) {
Api::xato('Validatsiya xatosi', 422, ['matn' => $xatolar]);
}
$this->repo->yangila((int) $id, $matn);
}
Api::javob(['data' => $this->formatla($this->repo->top((int) $id))]);
}
// DELETE /api/vazifalar/{id}
public function ochirish(string $id): void
{
$this->repo->top((int) $id) ?? Api::xato('Vazifa topilmadi', 404);
$this->repo->ochir((int) $id);
Api::javob(null, 204);
}
/** Bazadagi qatorni API formatiga o'girish */
private function formatla(array $v): array
{
return [
'id' => (int) $v['id'],
'matn' => $v['matn'],
'bajarildi' => (bool) $v['bajarildi'],
'yaratilgan' => (new \DateTimeImmutable($v['yaratilgan']))->format(DATE_ATOM),
];
}
}
formatla() muhim: bazadagi ustunlar to'g'ridan-to'g'ri tashqariga chiqmaydi. bajarildi — 0/1 emas, true/false. Sana — ISO 8601 formatida. Ertaga bazada ustun nomini o'zgartirsangiz — API mijozlari buzilmaydi.
Yo'llar
Routerga put, patch, delete metodlarini qo'shamiz (41-darsdagi get/post kabi), keyin:
<?php
use App\Controller\Api\VazifaApiController as VA;
$router->get('/api/vazifalar', [VA::class, 'royxat']);
$router->post('/api/vazifalar', [VA::class, 'yaratish']);
$router->get('/api/vazifalar/{id}', [VA::class, 'bitta']);
$router->patch('/api/vazifalar/{id}', [VA::class, 'yangilash']);
$router->delete('/api/vazifalar/{id}', [VA::class, 'ochirish']);
Sinab ko'rish
Brauzer faqat GET yuboradi. Qolganlari uchun curl (terminal), Postman yoki VS Code dagi REST Client kengaytmasi:
# Ro'yxat
curl http://localhost:8000/api/vazifalar
# Yaratish
curl -X POST http://localhost:8000/api/vazifalar \
-H "Content-Type: application/json" \
-d '{"matn": "Non olish"}'
# O'zgartirish
curl -X PATCH http://localhost:8000/api/vazifalar/1 \
-H "Content-Type: application/json" \
-d '{"matn": "Non va sut olish"}'
# O'chirish
curl -X DELETE http://localhost:8000/api/vazifalar/1 -i
Javoblar:
// 201 Created
{"data": {"id": 7, "matn": "Non olish", "bajarildi": false, "yaratilgan": "2026-11-02T14:30:00+05:00"}}
// 422 Unprocessable Content
{"xato": "Validatsiya xatosi", "tafsilot": {"matn": ["Vazifa matni 1-255 belgi bo'lsin"]}}
// 404 Not Found
{"xato": "Vazifa topilmadi"}
Autentifikatsiya — qisqacha
API da sessiya cookie kamdan-kam ishlatiladi (mobil ilovalarda noqulay). Odatda token:
curl http://localhost:8000/api/vazifalar -H "Authorization: Bearer 7f3a9c..."
<?php
$sarlavha = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (!preg_match('/^Bearer\s+(\S+)$/', $sarlavha, $m)) {
Api::xato('Token kerak', 401);
}
$stmt = db()->prepare('SELECT user_id FROM api_tokenlar WHERE token_hash = ?');
$stmt->execute([hash('sha256', $m[1])]);
$userId = $stmt->fetchColumn() ?: Api::xato("Token noto'g'ri", 401);
Token ham parol kabi — bazada xesh sifatida saqlanadi. (Bu yerda sha256 yetarli, chunki token uzun va tasodifiy — parol kabi taxmin qilib bo'lmaydi.) Laravel da buni Sanctum paketi tayyor qilib beradi.
CORS
Boshqa domendagi frontend (http://localhost:5173 dagi React) API ga so'rov yuborsa, brauzer uni bloklaydi. Ruxsat berish:
<?php
header('Access-Control-Allow-Origin: https://mening-frontend.uz');
header('Access-Control-Allow-Methods: GET, POST, PATCH, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
[!WARNING]
Access-Control-Allow-Origin: *— "har qanday sayt" degani. Ochiq, ma'lumotlari ommaviy API uchun normal. Foydalanuvchi ma'lumotlari bilan ishlaydigan API uchun — aniq domenlarni yozing.
Nima uchun shunday?
Nima uchun REST "fe'llarni URL ga yozmaslik" ni talab qiladi?
Chunki HTTP da fe'llar allaqachon bor — GET, POST, DELETE. Butun internet infratuzilmasi ularni tushunadi: proksi va CDN lar GET javoblarini keshlaydi, DELETE ni keshlamaydi; brauzer GET ni xavfsiz deb qayta yuboradi, POST uchun ogohlantiradi. /vazifaniOchir?id=5 ni GET bilan qilsangiz — bu infratuzilma sizga qarshi ishlaydi. REST — HTTP ni qanday yaratilgan bo'lsa, shunday ishlatish.
Xulosa
Eslab qoling:
- Resurs — ot, ko'plikda:
/api/vazifalar,/api/vazifalar/5 - Amal — HTTP metodi:
GET,POST,PATCH,DELETE - Status kodlar:
200,201,204,400,401,404,422 - JSON tana:
file_get_contents('php://input')+json_decode - Bazadagi qatorni to'g'ridan-to'g'ri emas — formatlangan holda qaytaring
- Token bilan autentifikatsiya, CORS — aniq domenlar
- Sinash — curl, Postman, REST Client
Keyingi dars
43-dars: Xavfsizlik — XSS, CSRF, SQL injection, sessiya o'g'irlash