Skip to Content
Course content

83: Building RESTful Routes Without a Framework

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I even know which URL was requested and what method was used?

When you aren't using a framework like Laravel or Symfony, you don't have a Route::get() helper. You're on your own. The first thing you need to realize is that PHP doesn't automatically "route" anything; it just executes a file. To build a REST API, we use the Front Controller pattern. This means we tell the web server (via .htaccess in Apache or a location block in Nginx) to send every single request to a single file: index.php.

Once everything hits index.php, you can inspect the global $_SERVER array. I usually start by grabbing the URI and the request method like this:

$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uriSegments = explode('/', trim($uri, '/'));

// Example: GET /books/12
// $method = 'GET'
// $uriSegments = ['books', '12']

It's simple, but it's the foundation. You're basically manually doing what the framework does behind the scenes.

What's the best way to handle those dynamic IDs in the path?

This is where things get tricky. If you're building a Book API, /books might list everything, but /books/12 needs to fetch one specific book. You can't just hardcode every possible ID. I prefer using a basic switch or a mapping array combined with the segments we exploded earlier.

Here is how I'd structure a basic router for a Book resource:

if ($uriSegments[0] === 'books') {
    $id = $uriSegments[1] ?? null;

    switch ($method) {
        case 'GET':
            $id ? handleGetBook($id) : handleListBooks();
            break;
        case 'POST':
            handleCreateBook();
            break;
        case 'PUT':
            if (!$id) throw new Exception("Book ID required for update", 400);
            handleUpdateBook($id);
            break;
        case 'DELETE':
            if (!$id) throw new Exception("Book ID required for deletion", 400);
            handleDeleteBook($id);
            break;
        default:
            http_response_code(405); // Method Not Allowed
            break;
    }
} else {
    http_response_code(404); // Not Found
}

I'll be honest: as your app grows, this switch statement will become a nightmare. But for a small-to-medium project, it's incredibly fast and easy to debug because there's no "magic" happening.

How do I actually get the JSON data out of a PUT or POST request?

Here is a common trap: many developers try to use $_POST. The problem is that $_POST only populates if the request is application/x-www-form-urlencoded or multipart/form-data. Most modern REST clients send application/json, which PHP ignores when filling the $_POST superglobal.

To get that data, you have to read the "raw" input stream from php://input. I usually wrap this in a small helper function so I don't have to rewrite the json_decode logic everywhere:

function getRequestBody() {
    $rawInput = file_get_contents('php://input');
    $data = json_decode($rawInput, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        // The JSON was malformed
        http_response_code(400);
        echo json_encode(['error' => 'Invalid JSON provided']);
        exit;
    }
    
    return $data;
}

// Usage inside your PUT handler:
function handleUpdateBook($id) {
    $input = getRequestBody();
    $title = $input['title'] ?? 'Untitled';
    // Now update the database...
    echo json_encode(['message' => "Book $id updated to $title"]);
}

Remember that php://input is a read-only stream. You can only read it once in some older PHP versions, though in modern PHP it's generally fine. Just get it once, decode it, and pass it along.




📋 Practical Task

Build a "Product Inventory" Route Handler

Your task is to create a standalone index.php file that implements a RESTful routing system for a product inventory. You don't need a real database—you can use a hardcoded array of products to simulate one.

  • Implement routing for the /products URI.
  • Handle a GET request to /products that returns a JSON list of all products.
  • Handle a GET request to /products/{id} that returns a specific product based on the ID provided in the URL.
  • Handle a DELETE request to /products/{id} that returns a JSON message confirming the deletion (e.g., {"message": "Product 5 deleted"}).
  • Ensure that if a user requests a URI other than /products, the server returns a 404 Not Found response.
  • Ensure that if a DELETE request is sent without an ID, the server returns a 400 Bad Request response.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.