A REST API
An API is a set of addresses that return not HTML pages but data in the JSON format. Mobile apps and JavaScript programs talk to them. In the REST approach each address denotes a resource, posts for example, and the HTTP method denotes the action on it. API controllers are best kept in a separate folder, app/Controllers/Api.
The controller
Create the file app/Controllers/Api/Posts.php by hand:
// app/Controllers/Api/Posts.php
namespace App\Controllers\Api;
use App\Models\PostModel;
use CodeIgniter\RESTful\ResourceController;
class Posts extends ResourceController
{
protected $modelName = PostModel::class;
protected $format = 'json';
public function index()
{
return $this->respond($this->model->findAll());
}
}The ResourceController class is a controller for APIs. The model from $modelName is available as $this->model, and the respond() method turns an array into a JSON response.
Routes
// app/Config/Routes.php
$routes->resource('api/posts', ['controller' => 'Api\Posts']);Open /api/posts and the browser shows the list of posts as JSON. One resource() line creates a whole set of routes:
| Request | Controller method |
|---|---|
GET /api/posts | index() |
GET /api/posts/5 | show(5) |
POST /api/posts | create() |
PUT /api/posts/5 | update(5) |
DELETE /api/posts/5 | delete(5) |
One record
public function show($id = null)
{
$post = $this->model->find($id);
if ($post === null) {
return $this->failNotFound('Post not found');
}
return $this->respond($post);
}The address /api/posts/1 returns one post, and /api/posts/999 returns the 404 code and an error message as JSON.
Creating
public function create()
{
$rules = [
'title' => 'required|min_length[5]',
'body' => 'required',
];
$data = $this->request->getJSON(true) ?? [];
if (! $this->validateData($data, $rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$id = $this->model->insert($this->validator->getValidated());
return $this->respondCreated($this->model->find($id));
}getJSON(true) reads the request body as an array. On failure the client receives a list of messages by field, on success the 201 code and the created record. Such a request can be tested with an API testing program or the curl command.
Response methods
| Method | Code |
|---|---|
respond($data) | 200 |
respondCreated($data) | 201 |
respondDeleted($data) | 200 |
respondNoContent() | 204 |
failValidationErrors($errors) | 400 |
failUnauthorized() | 401 |
failForbidden() | 403 |
failNotFound() | 404 |
ResponseTrait
The same methods are available in an ordinary controller through a trait — a set of methods added to a class:
// app/Controllers/Posts.php
use CodeIgniter\API\ResponseTrait;
class Posts extends BaseController
{
use ResponseTrait;
public function count()
{
return $this->respond(['total' => model(PostModel::class)->countAllResults()]);
}
}Protection
If the global csrf filter is on, POST requests to the API without a token are rejected. For API addresses it is switched off in app/Config/Filters.php: 'csrf' => ['except' => ['api/*']], and the routes themselves are closed with a filter that checks an access token — for example the tokens filter of the Shield package.
Common mistake
respond() sends everything the model returned, service columns included. For a users table that means the client receives password hashes. List the columns you need explicitly: $this->model->select('id, title, created_at')->findAll().