May's Blog

Alt Text

Before created API with prefixes. So I had API in the controller folder with everything else but when you have many controllers it can be hard to navigate between them. So I decided to move all API controllers to a new plugin that I called Api (the similarity of the names is purely random). And the second good thing on this is that you can have easily versions of your API. So here I using prefixes for API versioning (V1, V2 …).

How to do it

First time create new plugin

1php bin/cake.php bake plugin Api

And load it in Application.php bootstrap function.

1// src/Controller/AppController.php
2
3// add this to bootstrap function at the end
4$this->addPlugin('Api');

Next, you need to allow to use URL extensions, for me it is .json. You can also allow .xml if you need this. If you want more extensions check the official documentation for CakePHP. Open and edit your plugin’s AppController

1// /plugins/Api/src/Controller/AppController.php
2
3public function initialize(): void
4{
5    parent::initialize();
6
7    // Load Request handler
8    $this->loadComponent('RequestHandler');
9}

Update you plugin routing to allow Prefixes and extensions to look like following

 1// /plugins/Api/src/Plugin.php
 2
 3public function routes(RouteBuilder $routes): void
 4{
 5    $routes->plugin(
 6        'Api',
 7        ['path' => '/api'],
 8        function (RouteBuilder $builder) {
 9            // Add custom routes here
10
11            // API ver 1.0 routes
12            // add new block for more prefixes
13            $builder->prefix('V1', ['path' => '/v1'], function (RouteBuilder $routeBuilder) {;
14                $routeBuilder->setExtensions(['json']); // allow extensins. this have to be here for each prefix
15                $routeBuilder->fallbacks();
16            });
17
18            $builder->fallbacks();
19        }
20    );
21    parent::routes($routes);
22}

Update or create your actions in the API controller as following

 1// plugins/Api/src/Controller/V1/HelloController.php
 2
 3public function index()
 4{
 5    $this->Authorization->skipAuthorization();
 6
 7    $text = [
 8        'message' => 'Hello'
 9    ];
10
11    $this->set('text', $text);
12
13    // Add this to serialize repose
14    $this->viewBuilder()
15        ->setOption('serialize', 'text');
16}

You can test it with curl. And if everything goes OK you will see the result

1curl -i http://localhost:8765/api/v1/hello/index.json

Bonus If you want to post data to API you will be going to receive an error message as a response because the CSRF token doesn’t match. By default CakePHP 4 using Middleware so you have set it to skip verification CSRF for plugin routes. Here is how to do it.

Copy or write following function at the end to your Application.php

 1// src/Application.php
 2public function getCsrfProtectionMiddleware() : CsrfProtectionMiddleware
 3{
 4    $csrf = new CsrfProtectionMiddleware([
 5        'httponly' => true,
 6    ]);
 7
 8    $csrf->skipCheckCallback(function ($request) {
 9        if ($request->getParam('plugin') == 'Api') { // Skip Checking if plugin is API
10            return true;
11        }
12    });
13
14    return $csrf;
15}

Find and update loading csrf middleware from

1->add(new CsrfProtectionMiddleware([
2        'httponly' => true,
3  ]));

to this

1->add($this->getCsrfProtectionMiddleware());

and you can check with

1curl -i --data message="Hello from data" http://localhost:8765/api/v1/hello/index.json

To see your message from a post, update the index function do following

 1// plugins/Api/src/Controller/V1/HelloController.php
 2
 3public function index()
 4{
 5    $this->Authorization->skipAuthorization();
 6
 7    $text = [
 8        'message' => $this->request->getData('message'),
 9    ];
10
11    $this->set('text', $text);
12
13    // Add this to serialize repose
14    $this->viewBuilder()
15        ->setOption('serialize', 'text');
16}

That’s is how you can create APIs with CakePHP

Photo by Eve Lyn on Unsplash

#CakePHP #PHP