Building JSON APIs

Slim is an especially popular choice for building JSON REST APIs — it stays out of the way, gives you routing and middleware, and leaves the response format entirely up to you. Building a JSON API in Slim really comes down to two things: encoding data as JSON and writing it to the response body, and setting the right Content-Type header so clients know how to parse it.

A Reusable JSON Response Helper

Since almost every route in an API needs to do the same two steps — json_encode() the data and set the header — it's worth writing a small helper function once instead of repeating both lines in every route handler.

<?php
function jsonResponse($response, $data, int $status = 200) {
    $response->getBody()->write(json_encode($data));
    return $response
        ->withHeader('Content-Type', 'application/json')
        ->withStatus($status);
}

Every route below simply calls jsonResponse($response, $data) instead of repeating the encode-and-header dance by hand.

Worked Example: A Books Resource

A minimal REST resource for /api/books needs at least a way to list books and a way to create one. Here both routes are grouped together, as covered on the Route Groups page.

<?php
$app->group('/api/books', function ($group) {

    // GET /api/books - return every book as a JSON array
    $group->get('', function ($request, $response) {
        $books = [
            ['id' => 1, 'title' => 'PHP Basics'],
            ['id' => 2, 'title' => 'Learning Slim'],
        ];
        return jsonResponse($response, $books);
    });

    // POST /api/books - create a book from the JSON request body
    $group->post('', function ($request, $response) {
        $input = json_decode((string)$request->getBody(), true);

        if (empty($input['title'])) {
            return jsonResponse($response, ['error' => 'title is required'], 400);
        }

        $created = ['id' => 3, 'title' => $input['title']];

        // 201 Created, along with the resource that was just created
        return jsonResponse($response, $created, 201);
    });

});
Recall: Reading the JSON request body with json_decode((string)$request->getBody(), true) was introduced on the Request & Response page — the true flag decodes it as an associative array instead of an object.

Using the Right HTTP Status Code

A JSON API is only as useful as its status codes are accurate. Clients (and other developers) rely on the status code — not just the JSON body — to know what happened.

StatusMeaningWhen to use it
200 OKSuccessA GET, PUT, or DELETE completed normally
201 CreatedResource createdA POST successfully created a new resource — return the new resource too
400 Bad RequestInvalid inputThe request body is missing required fields or is malformed
404 Not FoundNo such resourceA GET/PUT/DELETE targets an id that doesn't exist
<?php
// Example: returning 404 when a specific book id isn't found
$group->get('/{id}', function ($request, $response, $args) {
    $book = null; // pretend a lookup happened and found nothing

    if ($book === null) {
        return jsonResponse($response, ['error' => 'Book not found'], 404);
    }

    return jsonResponse($response, $book);
});
Note: Always returning 200 — even for errors — forces API consumers to inspect the JSON body just to know if something failed. Correct status codes let them check the response status first.
Next up: Learn what happens when something goes wrong that isn't a simple validation error, in Error Handling.