Architecture and Requirements

The API will support the following operations:

  • GET /tasks: Retrieve all tasks.
  • GET /tasks/{id}: Retrieve a specific task.
  • POST /tasks: Create a new task.
  • PUT /tasks/{id}: Update an existing task.
  • DELETE /tasks/{id}: Delete a task.

We'll use PHP's native $_SERVER and $_GET/$_POST superglobals to simulate routing and request handling, without relying on external frameworks. For data storage, we'll use an in-memory array to represent a database.


Implementation

Step 1: Define the Task Resource

<?php

$tasks = [
    1 => ['id' => 1, 'title' => 'Design UI', 'completed' => false],
    2 => ['id' => 2, 'title' => 'Write documentation', 'completed' => true],
];

Step 2: Determine the HTTP Method and Route

$method = $_SERVER['REQUEST_METHOD'];
$request = explode('/', trim($_SERVER['PATH_INFO'], '/'));

switch ($request[0]) {
    case 'tasks':
        handleTasks($method, $request[1] ?? null);
        break;
    default:
        http_response_code(404);
        echo json_encode(['error' => 'Not found']);
        break;
}

function handleTasks($method, $id = null) {
    global $tasks;

    switch ($method) {
        case 'GET':
            if ($id) {
                if (isset($tasks[$id])) {
                    echo json_encode($tasks[$id]);
                } else {
                    http_response_code(404);
                    echo json_encode(['error' => 'Task not found']);
                }
            } else {
                echo json_encode(array_values($tasks));
            }
            break;

        case 'POST':
            $data = json_decode(file_get_contents('php://input'), true);
            if (!isset($data['title'])) {
                http_response_code(400);
                echo json_encode(['error' => 'Title is required']);
                return;
            }
            $id = count($tasks) + 1;
            $tasks[$id] = [
                'id' => $id,
                'title' => $data['title'],
                'completed' => $data['completed'] ?? false
            ];
            http_response_code(201);
            echo json_encode($tasks[$id]);
            break;

        case 'PUT':
            if (!$id || !isset($tasks[$id])) {
                http_response_code(404);
                echo json_encode(['error' => 'Task not found']);
                return;
            }
            $data = json_decode(file_get_contents('php://input'), true);
            if (!isset($data['title'])) {
                http_response_code(400);
                echo json_encode(['error' => 'Title is required']);
                return;
            }
            $tasks[$id]['title'] = $data['title'];
            $tasks[$id]['completed'] = $data['completed'] ?? $tasks[$id]['completed'];
            echo json_encode($tasks[$id]);
            break;

        case 'DELETE':
            if (!$id || !isset($tasks[$id])) {
                http_response_code(404);
                echo json_encode(['error' => 'Task not found']);
                return;
            }
            unset($tasks[$id]);
            echo json_encode(['message' => 'Task deleted']);
            break;

        default:
            http_response_code(405);
            echo json_encode(['error' => 'Method not allowed']);
            break;
    }
}

Best Practices in REST API Development

PracticeDescription
Use proper HTTP status codesReturn 200, 201, 400, 404, 405, etc., to indicate success or errors clearly.
Validate input dataEnsure required fields are present and sanitize inputs to avoid malicious data.
Use consistent JSON structureAlways return JSON with predictable keys like id, title, and completed.
Support pagination and filteringAdd ?page=2 or ?status=completed for large datasets.
Use middleware for authenticationProtect endpoints using tokens or session-based authentication.
Document your APIProvide usage examples and endpoint descriptions for other developers.

Comparison of REST API Methods

HTTP MethodDescriptionUse Case
GETRetrieve dataFetching a resource or list
POSTCreate dataAdding a new resource
PUTUpdate dataModifying an existing resource
DELETERemove dataDeleting a resource
PATCHPartial updateUpdating one or more fields

Note: This example does not use PATCH, but it can be implemented similarly to PUT with selective updates.


Error Handling and Response Structure

All API responses should include a consistent structure. A successful response might look like this:

{
  "id": 3,
  "title": "Test task",
  "completed": false
}

An error response should be:

{
  "error": "Title is required"
}

or

{
  "error": "Method not allowed"
}

Learn more with useful resources