Content Index
- The models
- Creating the resource type class for the Rest API
- Creating the Restful API in CodeIgniter 4
- Customize the response of the Rest Api
- Creating the Restful methods
- Get operations
- Post operations
- PUT or PATCH operations
- Delete operations
- Final code of the Rest
- Keys to defining a REST API
- Características Principales
- Main Features
- HTTP Status Codes
- Postman
- Paginate
- Search with filters
- List of movies by category
Remember to watch the complete video to create a Rest Api in CodeIgniter where we explore many more tricks that will surely help you a lot :)
We left off at the point where we already learned how to manage application routes and URLs in CodeIgniter 4.
The Rest APIs are a set of methods that can be consumed via GET, POST, PUT, PATCH, DELETE requests, in order to communicate our web application, in this case CodeIgniter 4 with other types of applications, Flutter, Vue and a long etc.
REMEMBER THAT a Rest API is not a protocol, it is not a standard, therefore it is not necessary to follow a particular pattern or specific order, you are free to define the methods as you see fit to perform the actions that you consider.
In short, it is a structure that we can create in our application to interconnect applications or systems, for example if you wanted to create a mobile application for your application, in our case a movie website, you can create your rest resources to share details, as well as being able to create and edit them in case you are interested in applying the complete CRUD from your mobile application; of course, it does not have to be exclusive to a mobile application with Flutter, Swift or Kotlin, it can be an application in Vue, React or any technology that allows you to consume this information through requests to your Rest Api.
In CodeIgniter we have it very easy to create a Rest Api, if at this point we manage to do a complete CRUD for our application, what we have to do now is import all that logic that we use in the controller and adapt it to our Rest Api; Rest Api that, as you can imagine, is also a class with the CRUD type functions that we explained above, what changes is the response, it will no longer be a view but a JSON or XML.
One of the features that the new version of CodeIgniter 4 brings with it is that we can now create a Rest API without the need to install third-party dependencies; For this, we have to create a class of type ResourceController in which we have to forcefully specify the model and the default format:
protected $modelName = 'App\Models\MovieModel'; protected $format = 'json';So, let's see how we can create a CRUD Rest Api or ApiRest using our CodeIgniter 4 (ResfFul).
The models
As you can see, when we define a controller of this type, of type Resource or ResourceController, we are binding a model to said class; In this case we are using the following model, which is the one we use in my CodeIgniter 4 course
<?php /blog/codeigniter/curso-para-dar-los-primeros-pasos-en-codeigniter-4-el-framework-php-del-futuro
namespace App\Models;
use CodeIgniter\Model;
class MovieModel extends Model {
protected $table = 'movies';
protected $primaryKey = 'id';
protected $allowedFields = ['title', 'description','category_id'];
}And this is used by the previous model:
<?php
namespace App\Models;
use CodeIgniter\Model;
class CategoryModel extends Model
{
protected $table = 'categories';
protected $primaryKey = 'id';
protected $allowedFields = ['title'];
}
Creating the resource type class for the Rest API
We are going to create a controller class of type resource for our RestApi that implements the operations of type CRUD:
<?php
use CodeIgniter\RESTful\ResourceController;
class RestMovie extends ResourceController
{
protected $modelName = 'App\Models\MovieModel';
protected $format = 'json';
}
As you can see, we have the common operations to manage an entity that in this case would be a Movie, but it can be anything, a Post, an article...
Creating the Restful API in CodeIgniter 4
Before starting to show the operations, remember that this is a resource type controller, therefore, all the methods that we are going to use for the CRUD have defined names to which their functions refer; for example, the function that is in charge of displaying a list is called index or that of displaying the detail of the show entity and so on for the rest.
Customize the response of the Rest Api
I'm going to create a helper function in this very class that will allow me to return responses in a custom format for easy consumption; items like:
- Data
- Response status code
- Message
They are components that I always like to return to the Rest response and this is to give more information and flexibility when working with this type of structure:
function genericResponse($data, $msj, $code)
{
if ($code == 200) {
return $this->respond(array("data" => $data, "code" => $code));
//, 404, "No hay nada"
} else {
return $this->respond(array("msj" => $msj, "code" => $code));
}
}Creating the Restful methods
Get operations
For the Get operations, which are the simplest and we can easily use test from our browser, they allow us to obtain a particular record, given the identifier and all the records, for this it is not necessary to pass any identifier:
public function index() {
return $this->genericResponse($this->model->findAll(), NULL, 200);
}
public function show($id = NULL) {
return $this->genericResponse($this->model->find($id), NULL, 200);
}Post operations
With this type of operation, you are interested in creating a new resource, in this case a movie, therefore, we have to do some verification, how to process or validate the data that we are going to receive, and its subsequent saving in the database. then in response we can simply return the created resource.
Remember that for this type of operation, we have to create a method or function called create:
public function create() {
$movie = new MovieModel();
$category = new CategoryModel();
if ($this->validate('movies')) {
if (!$this->request->getPost('category_id'))
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
if (!$category->get($this->request->getPost('category_id'))) {
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500); }
$id = $movie->insert([ 'title' => $this->request->getPost('title'), 'description' => $this->request->getPost('description'), 'category_id' => $this->request->getPost('category_id'), ]); return $this->genericResponse($this->model->find($id), NULL, 200); }
$validation = \Config\Services::validation();
return $this->genericResponse(NULL, $validation->getErrors(), 500);
}Here, as you can see, we are using a category since a movie belongs to a category, therefore we validate that it exists, and if it does not exist, we simply return an error response.
We also apply some validations through the validate function which is using the validations that we created in the corresponding configuration file for the validations and they look like this:
public $movies =[ 'title' => 'required|min_length[3]|max_length[255]', 'description' => 'min_length[3]|max_length[5000]' ];PUT or PATCH operations
For this type of request, which is to update a resource, in this case a movie, it can be the same scheme that we saw before to create a resource, since we need to validate the data that we are going to receive but this time we are also going to receive an identifier for the url that will allow us to update this resource, otherwise it is similar to a request of the post type:
public function update($id = NULL)
{
$movie = new MovieModel();
$category = new CategoryModel();
$data = $this->request->getRawInput();
if ($this->validate('movies')) {
if (!$data['category_id'])
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
if (!$movie->get($id)) {
return $this->genericResponse(NULL, array("movie_id" => "Película no existe"), 500);
}
if (!$category->get($data['category_id'])) {
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
}
$movie->update($id, ['title' => $data['title'], 'description' => $data['description'], 'category_id' => $data['category_id'],]);
return $this->genericResponse($this->model->find($id), NULL, 200);
}
}Delete operations
In this case, we are interested in developing a simple function that simply receives an identifier to be able to delete a record, in order to use it, we need to send a request of type delete from our http.
This has been one of the simplest operations, if not the simplest that we can perform:
public function delete($id = NULL)
{
$movie = new MovieModel();
$movie->delete($id);
return $this->genericResponse("Producto eliminado", NULL, 200);
}Final code of the Rest
In the end, our code, our Restful class looks like this:
<?php
namespace App\Controllers;
use App\Models\CategoryModel;
use App\Models\MovieModel;
use CodeIgniter\RESTful\ResourceController;
class RestMovie extends ResourceController
{
protected $modelName = 'App\Models\MovieModel';
protected $format = 'json';
public function index()
{
return $this->genericResponse($this->model->findAll(), NULL, 200);
}
public function show($id = NULL)
{
return $this->genericResponse($this->model->find($id), NULL, 200);
}
public function delete($id = NULL)
{
$movie = new MovieModel();
$movie->delete($id);
return $this->genericResponse("Producto eliminado", NULL, 200);
}
public function create()
{
$movie = new MovieModel();
$category = new CategoryModel();
if ($this->validate('movies')) {
if (!$this->request->getPost('category_id')) return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
if (!$category->get($this->request->getPost('category_id'))) {
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
}
$id = $movie->insert(['title' => $this->request->getPost('title'), 'description' => $this->request->getPost('description'), 'category_id' => $this->request->getPost('category_id'),]);
return $this->genericResponse($this->model->find($id), NULL, 200);
}
$validation = \Config\Services::validation();
return $this->genericResponse(NULL, $validation->getErrors(), 500);
}
public function update($id = NULL)
{
$movie = new MovieModel();
$category = new CategoryModel();
$data = $this->request->getRawInput();
if ($this->validate('movies')) {
if (!$data['category_id']) return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
if (!$movie->get($id)) {
return $this->genericResponse(NULL, array("movie_id" => "Película no existe"), 500);
}
if (!$category->get($data['category_id'])) {
return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
}
$movie->update($id, ['title' => $data['title'], 'description' => $data['description'], 'category_id' => $data['category_id'],]);
return $this->genericResponse($this->model->find($id), NULL, 200);
}
$validation = \Config\Services::validation();
return $this->genericResponse(NULL, $validation->getErrors(), 500);
}
private function genericResponse($data, $msj, $code)
{
if ($code == 200) {
return $this->respond(array("data" => $data, "code" => $code)); //, 404, "No hay nada"
} else {
return $this->respond(array("msj" => $msj, "code" => $code));
}
}
}Keys to defining a REST API
Key changes compared to a traditional controller:
- No Sessions: A REST API is stateless. We don't use sessions for success messages or to maintain data.
- No Redirects: Instead of redirect(), we return a response with the appropriate HTTP status code.
- Status Codes: If validation fails, we return a 400 (Bad Request) code along with the errors.
Características Principales
ResourceController: Al heredar de esta clase, CodeIgniter nos facilita herramientas específicas para APIs RESTful.
- $modelName: Al definir el nombre del modelo aquí, el framework crea automáticamente una instancia accesible mediante $this->model, haciendo el código más limpio.
- $format: Aquí establecemos el formato por defecto. Aunque XML es una opción, JSON es el estándar más utilizado por ser ligero y flexible.
- respond(): Esta función sustituye a view(). Se encarga de castear los datos al formato elegido y enviar la respuesta HTTP correcta.
Main Features
ResourceController: By inheriting from this class, CodeIgniter provides us with specific tools for RESTful APIs.
- $modelName: By defining the model name here, the framework automatically creates an instance accessible via $this->model, resulting in cleaner code.
- $format: Here we set the default format. Although XML is an option, JSON is the most widely used standard due to its lightweight and flexible nature.
- respond(): This function replaces view(). It handles casting the data to the chosen format and sending the correct HTTP response.
HTTP Status Codes
A vital aspect of APIs is the use of status codes. These allow the client application (e.g., Flutter or Vue) to know exactly what happened without needing to read the text message:
- 200 OK: Successful request.
- 201 Created: Resource created successfully.
- 400 Bad Request: Invalid data.
- 401 Unauthorized: The user is not authenticated.
- 404 Not Found: The resource does not exist.
- 500 Internal Server Error: Server error.
Postman
Postman is an application designed to test and experience the APIs that we are creating through a simple interface.
Postman is available for Windows, Mac, and Linux, so you can use it on the most popular operating systems on the market.
Postman is software that, in a few words, allows us to easily test all types of requests with a simple interface that, through forms, we can compose the request that we want to send to our application; in order to send a request to CodeIgniter from Postman, we have to compose the request as follows:
- Set the type: POST, PUT, PATCH, DELETE or GET
- If you are going to submit data by form, set it to Body -> x-www-form-urlencoded and set the data.
Remember that you can have a list of the URIs of our Rest functions with the command:
$ php spark routesFor routes of type rest, we will see:
GET / \App\Controllers\Home::index
GET api/pelicula \App\Controllers\Api\Pelicula::index
GET api/pelicula/new \App\Controllers\Api\Pelicula::new
GET api/pelicula/(.*)/edit \App\Controllers\Api\Pelicula::edit/$1
GET api/pelicula/(.*) \App\Controllers\Api\Pelicula::show/$1
POST api/pelicula \App\Controllers\Api\Pelicula::create
PATCH api/pelicula/(.*) \App\Controllers\Api\Pelicula::update/$1
PUT api/pelicula/(.*) \App\Controllers\Api\Pelicula::update/$1
DELETE api/pelicula/(.*) \App\Controllers\Api\Pelicula::delete/$1 Finally we send an invalid request:

Look in the headers section for the status code we set; in our case:
Status: 400 Bad Request
And if it is valid:

We will see the answer that everything is ok.
Paginate
We can create a function that returns the paginated list, as we have worked on both the dashboard and the user module:
app\Controllers\Api\Pelicula.php
public function paginado()
{
return $this->respond($this->model->paginate(10));
}And the route:
app\Config\Routes.php
$routes->group('api', ['namespace' => 'App\Controllers\Api'], function ($routes) {
$routes->get('pelicula/paginado', 'Pelicula::paginado');
***
});Search with filters
For the filters by categories, search term and label, we will use the one from the previous chapter:
app\Controllers\Api\Pelicula.php
public function paginado_full()
{
$peliculas = $this->model
->when($this->request->getGet('buscar'), static function ($query, $buscar) {
$query->groupStart()->orLike('peliculas.titulo', $buscar, 'both');
$query->orLike('peliculas.descripcion', $buscar, 'both')->groupEnd();
})
->when($this->request->getGet('categoria_id'), static function ($query, $categoriaId) {
$query->where('peliculas.categoria_id', $categoriaId);
})
->when($this->request->getGet('etiqueta_id'), static function ($query, $etiquetaId) {
$query->where('etiquetas.id', $etiquetaId);
})
->select('peliculas.*, categorias.titulo as categoria, GROUP_CONCAT(DISTINCT(etiquetas.titulo)) as etiquetas, MAX(imagenes.imagen) as imagen')
->join('categorias', 'categorias.id = peliculas.categoria_id')
->join('pelicula_imagen', 'pelicula_imagen.pelicula_id = peliculas.id', 'left')
->join('imagenes', 'imagenes.id = pelicula_imagen.imagen_id', 'left')
->join('pelicula_etiqueta', 'pelicula_etiqueta.pelicula_id = peliculas.id', 'left')
->join('etiquetas', 'etiquetas.id = pelicula_etiqueta.etiqueta_id', 'left');
$peliculas = $peliculas->groupBy('peliculas.id');
$peliculas = $peliculas->paginate();
return $this->respond($peliculas);
}And the route:
app\Config\Routes.php
$routes->group('api', ['namespace' => 'App\Controllers\Api'], function ($routes) {
$routes->get('pelicula/paginado', 'Pelicula::paginado');
$routes->get('pelicula/paginado_full', 'Pelicula::paginado_full');
***
});List of movies by category
An additional controller to get a list of movies given the category:
app\Controllers\Api\Pelicula.php
public function index_por_categoria($categoriaId)
{
$peliculas = $this->model
->select('peliculas.*, categorias.titulo as categoria, GROUP_CONCAT(DISTINCT(etiquetas.titulo)) as etiquetas, MAX(imagenes.imagen) as imagen')
->join('categorias', 'categorias.id = peliculas.categoria_id')
->join('pelicula_imagen', 'pelicula_imagen.pelicula_id = peliculas.id', 'left')
->join('imagenes', 'imagenes.id = pelicula_imagen.imagen_id', 'left')
->join('pelicula_etiqueta', 'pelicula_etiqueta.pelicula_id = peliculas.id', 'left')
->join('etiquetas', 'etiquetas.id = pelicula_etiqueta.etiqueta_id', 'left')
->where('peliculas.categoria_id', $categoriaId)
->groupBy('peliculas.id')->paginate();
return $this->respond($peliculas);
}And the route:
app\Config\Routes.php
$routes->group('api', ['namespace' => 'App\Controllers\Api'], function ($routes) {
$routes->get('pelicula/index_por_categoria/(:num)', 'Pelicula::index_por_categoria/$1');
***
});The next step we need to take is to consume the app in CodeIgniter 4 from an application; for this, we need to know how to use CORS in CodeIgniter 4.