REST

REST is a foundational concept to the open web. CakePHP provides functionality to build applications that expose REST APIs with low complexity abstractions and interfaces.

CakePHP provides methods for exposing your controller actions via HTTP methods, and serializing view variables based on content-type negotiation. Content-Type negotiation allows clients of your application to send requests with serialize data and receive responses with serialized data via the Accept and Content-Type headers, or URL extensions.

Getting Started

To get started with adding a REST API to your application, we’ll first need a controller containing actions that we want to expose as an API. A basic controller might look something like this:

// src/Controller/RecipesController.php
use Cake\View\JsonView;

class RecipesController extends AppController
{
    public function viewClasses(): array
    {
        return [JsonView::class];
    }

    public function index()
    {
        $recipes = $this->Recipes->find('all')->all();
        $this->set('recipes', $recipes);
        $this->viewBuilder()->setOption('serialize', ['recipes']);
    }

    public function view($id)
    {
        $recipe = $this->Recipes->get($id);
        $this->set('recipe', $recipe);
        $this->viewBuilder()->setOption('serialize', ['recipe']);
    }

    public function add()
    {
        $this->request->allowMethod(['post', 'put']);
        $recipe = $this->Recipes->newEntity($this->request->getData());
        if ($this->Recipes->save($recipe)) {
            $message = 'Saved';
        } else {
            $message = 'Error';
        }
        $this->set([
            'message' => $message,
            'recipe' => $recipe,
        ]);
        $this->viewBuilder()->setOption('serialize', ['recipe', 'message']);
    }

    public function edit($id)
    {
        $this->request->allowMethod(['patch', 'post', 'put']);
        $recipe = $this->Recipes->get($id);
        $recipe = $this->Recipes->patchEntity($recipe, $this->request->getData());
        if ($this->Recipes->save($recipe)) {
            $message = 'Saved';
        } else {
            $message = 'Error';
        }
        $this->set([
            'message' => $message,
            'recipe' => $recipe,
        ]);
        $this->viewBuilder()->setOption('serialize', ['recipe', 'message']);
    }

    public function delete($id)
    {
        $this->request->allowMethod(['delete']);
        $recipe = $this->Recipes->get($id);
        $message = 'Deleted';
        if (!$this->Recipes->delete($recipe)) {
            $message = 'Error';
        }
        $this->set('message', $message);
        $this->viewBuilder()->setOption('serialize', ['message']);
    }
}

In our RecipesController, we have several actions that define the logic to create, edit, view and delete recipes. In each of our actions we’re using the serialize option to tell CakePHP which view variables should be serialized when making API responses. We’ll connect our controller to the application URLs with RESTful Routing:

// in config/routes.php
$routes->scope('/', function (RouteBuilder $routes): void {
    $routes->setExtensions(['json']);
    $routes->resources('Recipes');
});

These routes will enable URLs like /recipes.json to return a JSON encoded response. Clients could also make a request to /recipes with the Content-Type: application/json header as well.

Encoding Response Data

In the above controller, we’re defining a viewClasses() method. This method defines which views your controller has available for content-negotitation. We’re including CakePHP’s JsonView which enables JSON based responses. To learn more about it and Xml based views see JSON and XML views. is used by CakePHP to select a view class to render a REST response with.

Next, we have several methods that expose basic logic to create, edit, view and delete recipes. In each of our actions we’re using the serialize option to tell CakePHP which view variables should be serialized when making API responses.

If we wanted to modify the data before it is converted into JSON we should not define the serialize option, and instead use template files. We would place the REST templates for our RecipesController inside templates/Recipes/json.

See the Content Type Negotiation for more information on how CakePHP’s response negotiation functionality.

Parsing Request Bodies

Creating the logic for the edit action requires another step. Because our resources are serialized as JSON it would be ergonomic if our requests also contained the JSON representation.

In our Application class ensure the following is present:

$middlewareQueue->add(new BodyParserMiddleware());

This middleware will use the content-type header to detect the format of request data and parse enabled formats. By default only JSON parsing is enabled by default. You can enable XML support by enabling the xml constructor option. When a request is made with a Content-Type of application/json, CakePHP will decode the request data and update the request so that $request->getData() contains the parsed body.

You can also wire in additional deserializers for alternate formats if you need them, using BodyParserMiddleware::addParser().