PHP REST API — Complete Guide to Building RESTful APIs
In this tutorial, you will learn about PHP REST API. We cover key concepts, practical examples, and best practices to help you master this topic.
PHP REST APIs handle HTTP requests and return JSON responses, implementing resource-based CRUD operations following RESTful principles for web and mobile clients.
What You'll Learn
By the end of this tutorial, you'll build RESTful API endpoints, handle JSON input/output, implement proper status codes, validate requests, and structure PHP APIs for scalability.
Why REST APIs Matter
REST is the standard architecture for web APIs. Mobile apps, SPAs, and third-party integrations communicate through RESTful endpoints. PHP powers millions of REST APIs worldwide.
Real-World Use
A mobile shopping app calls a PHP REST API for products, cart, checkout, and user profile. The API returns JSON consumed by both iOS and Android apps.
REST API Learning Path
flowchart LR
A[MVC] --> B[REST API]
B --> C[JWT Auth]
C --> D[Testing]
D --> E[Security]
A --> F{You Are Here}
style F fill:#f90,color:#fff
API Entry Point
<?php
// api/index.php
header("Content-Type: application/json; charset=utf-8");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
http_response_code(204);
exit;
}
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$uri = preg_replace("#^/api#", "", $uri);
$method = $_SERVER["REQUEST_METHOD"];
Router for API
<?php
function jsonResponse(mixed $data, int $code = 200): void {
http_response_code($code);
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
function getJsonBody(): array {
return json_decode(file_get_contents("php://input"), true) ?? [];
}
$routes = [
"GET" => ["/products" => "getProducts", "/products/(\d+)" => "getProduct"],
"POST" => ["/products" => "createProduct"],
"PUT" => ["/products/(\d+)" => "updateProduct"],
"DELETE" => ["/products/(\d+)" => "deleteProduct"],
];
$handler = null;
$params = [];
foreach ($routes[$method] ?? [] as $pattern => $action) {
if (preg_match("#^$pattern$#", $uri, $matches)) {
$handler = $action;
$params = array_slice($matches, 1);
break;
}
}
if (!$handler) jsonResponse(["error" => "Not Found"], 404);
CRUD Handlers
<?php
$products = [
1 => ["id" => 1, "name" => "Laptop", "price" => 999.99],
2 => ["id" => 2, "name" => "Mouse", "price" => 29.99],
];
$nextId = 3;
function getProducts(): void {
global $products;
jsonResponse(array_values($products));
}
function getProduct(int $id): void {
global $products;
if (!isset($products[$id])) jsonResponse(["error" => "Not found"], 404);
jsonResponse($products[$id]);
}
function createProduct(): void {
global $products, $nextId;
$data = getJsonBody();
if (empty($data["name"]) || !isset($data["price"])) {
jsonResponse(["error" => "Name and price required"], 400);
}
$products[$nextId] = ["id" => $nextId, "name" => $data["name"], "price" => (float)$data["price"]];
jsonResponse($products[$nextId], 201);
}
$handler(...$params);
Input Validation
<?php
function validateProduct(array $data): array {
$errors = [];
if (empty($data["name"])) $errors[] = "Product name is required";
if (!isset($data["price"])) $errors[] = "Price is required";
elseif ($data["price"] < 0) $errors[] = "Price must be positive";
if (!empty($errors)) {
jsonResponse(["error" => "Validation failed", "details" => $errors], 422);
}
return $data;
}
Error Handling
<?php
set_error_handler(function($severity, $message, $file, $line) {
jsonResponse(["error" => "Server error"], 500);
});
set_exception_handler(function(Throwable $e) {
jsonResponse(["error" => $e->getMessage()], 500);
});
Common Mistakes
1. Not Setting CORS Headers
Browser-based clients can't access APIs without CORS headers. Always set Access-Control-Allow-Origin.
2. Returning HTML Instead of JSON
PHP defaults to HTML content type. Set header("Content-Type: application/json") explicitly.
3. Using GET for State Changes
GET should be read-only. Use POST for creation, PUT/PATCH for updates, DELETE for removal.
4. Inconsistent Error Format
Always return errors in a consistent format: { error: string, code: number }.
5. Not Handling OPTIONS Preflight
Browsers send OPTIONS requests before cross-origin POST/PUT/DELETE. Handle them properly.
Practice Questions
1. What HTTP methods map to CRUD operations?
POST (Create), GET (Read), PUT/PATCH (Update), DELETE (Delete).
2. Why return proper HTTP status codes?
Clients use status codes to determine success/failure. 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Server Error.
3. How do you parse JSON request bodies?
Use json_decode(file_get_contents("php://input"), true) to get the request body as an array.
4. What is CORS and why is it needed?
Cross-Origin Resource Sharing. Browsers block cross-origin requests by default. CORS headers tell the browser which origins are allowed.
5. Challenge: Build a complete REST API endpoint for a todo list with CRUD.
<?php
header("Content-Type: application/json");
$todos = [["id" => 1, "title" => "Learn PHP", "completed" => false]];
$method = $_SERVER["REQUEST_METHOD"];
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if ($method === "GET" && $uri === "/api/todos") {
echo json_encode($todos);
} elseif ($method === "POST" && $uri === "/api/todos") {
$data = json_decode(file_get_contents("php://input"), true);
$todos[] = ["id" => count($todos) + 1, "title" => $data["title"], "completed" => false];
http_response_code(201);
echo json_encode(end($todos));
}
FAQ
Mini Project: RESTful Products API
Build a complete REST API for managing products with file-based storage.
<?php
header("Content-Type: application/json");
$dataFile = __DIR__ . "/products.json";
$products = file_exists($dataFile) ? json_decode(file_get_contents($dataFile), true) : [];
$method = $_SERVER["REQUEST_METHOD"];
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if ($method === "GET" && $uri === "/api/products") {
echo json_encode(array_values($products));
} elseif ($method === "POST" && $uri === "/api/products") {
$data = json_decode(file_get_contents("php://input"), true);
$id = count($products) + 1;
$products[$id] = ["id" => $id, "name" => $data["name"], "price" => $data["price"]];
file_put_contents($dataFile, json_encode($products));
http_response_code(201);
echo json_encode($products[$id]);
}
What's Next
PHP JWT Auth PHP Testing PHPUnit PHP Security
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro