Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

REST API

An API is a set of addresses that return data in JSON format instead of pages: it is read by a mobile app or JavaScript code. REST is a common way to build an API where the address names a resource (articles) and the HTTP method names the action on it. CakePHP has resource routes and a JSON view that needs no templates.

Routes

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

This block goes inside the main function of the file next to scope('/'). The single resources() line creates five routes:

RequestAction
GET /api/articlesindex()
GET /api/articles/5view(5)
POST /api/articlesadd()
PUT /api/articles/5edit(5)
DELETE /api/articles/5delete(5)

JSON response

// src/Controller/ArticlesController.php
use Cake\View\JsonView;
 
public function viewClasses(): array
{
    return [JsonView::class];
}
 
public function index(): void
{
    $articles = $this->Articles->find()->all();
 
    $this->set(compact('articles'));
    $this->viewBuilder()->setOption('serialize', ['articles']);
}

viewClasses() allows the controller to answer in JSON. The serialize option names the variables that go into the response. Open /api/articles.json: instead of a page you get an object with the articles key and an array of articles. No template is needed.

One record

public function view(int $id): void
{
    $article = $this->Articles->get($id);
 
    $this->set(compact('article'));
    $this->viewBuilder()->setOption('serialize', ['article']);
}

If the article does not exist, get() returns a 404 error, also in JSON.

Receiving data

An API client sends data in the request body as JSON. For getData() to understand it, a middleware is added to the middleware() method of src/Application.php:

use Cake\Http\Middleware\BodyParserMiddleware;
 
->add(new BodyParserMiddleware())

After that the create action looks familiar:

public function add(): void
{
    $article = $this->Articles->newEntity($this->request->getData());
 
    if ($this->Articles->save($article)) {
        $this->response = $this->response->withStatus(201);
        $this->set(compact('article'));
        $this->viewBuilder()->setOption('serialize', ['article']);
 
        return;
    }
 
    $this->response = $this->response->withStatus(422);
    $this->set('errors', $article->getErrors());
    $this->viewBuilder()->setOption('serialize', ['errors']);
}

On success the client gets the created article and the 201 code; on a validation error it gets the 422 code and messages per field.

Checking from the terminal

curl -H "Accept: application/json" http://localhost:8765/api/articles/1

The Accept header works the same way as the .json ending in the address.

Hidden fields

All fields of an entity go into the response. Those that must not be exposed are listed in the entity class:

// src/Model/Entity/User.php
protected array $_hidden = ['password'];

CSRF protection

The CSRF token check is designed for forms and rejects POST requests from an API client. For /api addresses it is turned off and the user is checked with a token in a header; the Authentication plugin can do that too.

Common mistake

Without $_hidden the API returns the whole record, including the password hash. And related data from contain() goes into the response with all of its fields. Look at the response of every route and hide everything the client does not need to know.

Thanks for reading!

Donate to the site — we work hard for you.