Published on

How to prefix route a CAKEPHP 3 REST API

In this follow-up post to How to use a CakePHP 3 REST API we will implement a Prefix Route so we can benefit from clean controller logic separation and limit access to our API resources through (only) /api prefixed URLs.

Run in Postman

Before We Begin

This is part three of the CakePHP 3 REST API tutorial series:

  1. How to build a CakePHP 3 REST API in minutes
  2. How to use a CakePHP 3 REST API
  3. How to prefix route a CakePHP 3 REST API
  4. How to add JWT Authentication to a CakePHP 3 REST API
  5. How to make your CakePHP 3 API produce JSON API
  6. How to use a CakePHP API as the data backend for Ember in 30 minutes

Before starting this tutorial either:

Creating the Prefix Route

1. Remove previous routing

Remove the general API routing created in the previous tutorial by removing these lines from your config/routes.php file:

Router::extensions(['json', 'xml']);
$routes->resources('Cocktails');

2. Create the prefix route

Now create a new /api prefixed route we will use for all of our API resources by adding the following code directly above the / scope definition in your config/routes.php file:

Router::prefix('api', function ($routes) {
    $routes->extensions(['json', 'xml']);
    $routes->resources('Cocktails');
});

Please note that once again:

  • Enabling extensions is optional
  • Only explicitely specified controllers will be accessible through your API

3. Create the prefix AppController

One of the great things about CakePHP 3 prefix routing is the automatic mapping to sub-namespaces which allows us to separate concern by creating dedicated controller files for (just) our API logic inside the src/Controller/Api subdirectory.

To optimize separation even further we will create an AppController dedicated to the Api namespace that all controllers in the Api namespace will extend (instead of extending the application's AppController).

Create a new file named src/Controller/Api/AppController.php with the following content:

<?php
namespace App\Controller\Api;

use Cake\Controller\Controller;
use Cake\Event\Event;

class AppController extends Controller
{
    use \Crud\Controller\ControllerTrait;

    public $components = [
        'RequestHandler',
        'Crud.Crud' => [
            'actions' => [
                'Crud.Index',
                'Crud.View',
                'Crud.Add',
                'Crud.Edit',
                'Crud.Delete'
            ],
            'listeners' => [
                'Crud.Api',
                'Crud.ApiPagination',
                'Crud.ApiQueryLog'
            ]
        ]
    ];
}

4. Create the CocktailsController

Now create a new controller file for your cocktail resources named src/Controller/Api/CocktailsController.php with the same content as used in the previous tutorial but this time:

  • Using the Api sub-namespace matching our prefix route
  • Extending App\Controller\Api\AppController
<?php
namespace App\Controller\Api;

use App\Controller\Api\AppController;

class CocktailsController extends AppController
{
    public $paginate = [
        'page' => 1,
        'limit' => 5,
        'maxLimit' => 15,
        'sortWhitelist' => [
            'id', 'name'
        ]
    ];
}

5. Cleanup

Even though this is optional we will remove all CRUD Api configuration from src/Controller/AppController.php to prove that only controller logic in the Api namespace is being used when we test the prefix.

<?php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller {

    use \Crud\Controller\ControllerTrait;

    public $components = [
        'Crud.Crud' => [
            'actions' => [
                'Crud.Index',
                'Crud.View',
                'Crud.Add',
                'Crud.Edit',
                'Crud.Delete'
            ]
        ]
    ];
}

Testing the Routes

1. Test API access

If things went well you should now be able to access your cocktail API resources using the /api prefix route.

To verify query http://cake3api.app/api/cocktails.json which should produce the familiar JSON response:

{
    "success": true,
    "data": [
        {
            "id": 1,
            "name": "Cosmopolitan",
            "description": "Vodka based"
        },
        {
            "id": 2,
            "name": "Margarita",
            "description": "Tequila based"
        },
        {
            "id": 3,
            "name": "Mojito",
            "description": "Rum based"
        }
    ],
    "pagination": {
        "page_count": 5,
        "current_page": 1,
        "has_next_page": true,
        "has_prev_page": false,
        "count": 21,
        "limit": null
    }
}
See the previous tutorial if you need more usage examples.

2. Test API access restrictions

If things went well your API resources should no longer be accessible using the default routes.

To verify query http://cake3api.app/cocktails.json as used in the previous tutorial. If things went will it:

  • Should no longer produce a JSON/XML response
  • Should instead show an HTML MissingController exception similar to the one below
Cocktails index after prefixing route

3. Test non-API access

Even though we have restricted API access to the /api prefix all non-API requests should still function as expected.

To verify browe to http://cake3api.app/cocktails which should still produce the familiar CakePHP index page:

Cocktails index

Additional reading

Hat tip to CakePHP Core Developer Ceeram for proofing this post.