Как написать REST API на PHP за 30 минут
REST API (Representational State Transfer) — это архитектурный стиль взаимодействия компонентов распределённого приложения в сети. В современной веб-разработке REST API является стандартом для создания бэкенда мобильных приложений, SPA (Single Page Application) и микросервисов. В этой статье мы покажем, как написать простой, но полностью рабочий REST API на PHP всего за 30 минут.
Мы создадим API для управления списком задач (Todo List) с поддержкой CRUD-операций (Create, Read, Update, Delete). Вы узнаете, как организовать маршрутизацию, обрабатывать JSON-запросы и подключать базу данных MySQL.
Подготовка окружения
Для работы вам понадобится:
- Веб-сервер Apache или Nginx с поддержкой PHP 7.4+
- MySQL или MariaDB
- Редактор кода (VS Code, PhpStorm)
- Postman или curl для тестирования API
Создайте структуру проекта:
todo-api/
├── index.php # Точка входа (роутер)
├── config.php # Настройки подключения к БД
├── api/
│ └── tasks.php # Логика работы с задачами
└── .htaccess # Настройки перезаписи URL
Шаг 1: Настройка .htaccess и точки входа
Создайте файл .htaccess для перенаправления всех запросов на index.php:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
Теперь создадим index.php — наш главный роутер:
<?php
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// Обработка preflight запросов
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
require_once 'config.php';
require_once 'api/tasks.php';
$url = isset($_GET['url']) ? rtrim($_GET['url'], '/') : '';
$urlParts = explode('/', $url);
$resource = $urlParts[0] ?? '';
$id = $urlParts[1] ?? null;
switch ($resource) {
case 'tasks':
$tasks = new Tasks();
echo $tasks->handleRequest($_SERVER['REQUEST_METHOD'], $id);
break;
default:
http_response_code(404);
echo json_encode(["message" => "Resource not found"]);
break;
}
?>
Шаг 2: Подключение к базе данных
Создайте config.php с настройками подключения:
<?php
class Database {
private $host = "localhost";
private $db_name = "todo_api";
private $username = "root";
private $password = "";
public $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name,
$this->username,
$this->password
);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
http_response_code(500);
echo json_encode(["message" => "Database connection error"]);
exit();
}
return $this->conn;
}
}
?>
Создайте таблицу в MySQL:
CREATE DATABASE todo_api;
USE todo_api;
CREATE TABLE tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
status ENUM('pending', 'completed') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Шаг 3: Реализация CRUD-операций
Создайте файл api/tasks.php с основной логикой:
<?php
class Tasks {
private $conn;
private $table = "tasks";
public function __construct() {
$database = new Database();
$this->conn = $database->getConnection();
}
public function handleRequest($method, $id) {
switch ($method) {
case 'GET':
return $id ? $this->getTask($id) : $this->getAllTasks();
case 'POST':
return $this->createTask();
case 'PUT':
return $this->updateTask($id);
case 'DELETE':
return $this->deleteTask($id);
default:
http_response_code(405);
return json_encode(["message" => "Method not allowed"]);
}
}
// Получить все задачи
private function getAllTasks() {
$query = "SELECT * FROM " . $this->table . " ORDER BY created_at DESC";
$stmt = $this->conn->prepare($query);
$stmt->execute();
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
http_response_code(200);
return json_encode($tasks);
}
// Получить одну задачу по ID
private function getTask($id) {
$query = "SELECT * FROM " . $this->table . " WHERE id = :id";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':id', $id);
$stmt->execute();
$task = $stmt->fetch(PDO::FETCH_ASSOC);
if ($task) {
http_response_code(200);
return json_encode($task);
} else {
http_response_code(404);
return json_encode(["message" => "Task not found"]);
}
}
// Создать новую задачу
private function createTask() {
$data = json_decode(file_get_contents("php://input"));
if (!isset($data->title) || empty(trim($data->title))) {
http_response_code(400);
return json_encode(["message" => "Title is required"]);
}
$query = "INSERT INTO " . $this->table . " (title, description) VALUES (:title, :description)";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':title', $data->title);
$stmt->bindParam(':description', $data->description ?? '');
if ($stmt->execute()) {
$lastId = $this->conn->lastInsertId();
http_response_code(201);
return json_encode([
"message" => "Task created",
"id" => $lastId
]);
} else {
http_response_code(500);
return json_encode(["message" => "Unable to create task"]);
}
}
// Обновить задачу
private function updateTask($id) {
$data = json_decode(file_get_contents("php://input"));
if (!$id) {
http_response_code(400);
return json_encode(["message" => "ID is required"]);
}
$