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:
| Request | Action |
|---|---|
GET /api/articles | index() |
GET /api/articles/5 | view(5) |
POST /api/articles | add() |
PUT /api/articles/5 | edit(5) |
DELETE /api/articles/5 | delete(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/1The 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.