Content Index
- The Importance of the Controller
- Composition of Routes
- Route Types
- Defining Routes in Config/Routes.php
- Creating a Controller and the use of Namespaces
- Grouping Routes
- Parameters for Routes and URLs
- Placeholders
- Named Routes
- Defining our routes for a CRUD operation
- Common Routes
- View Generated Routes
- Generate Routes Automatically
- Recommended CRUD Organization
- Method: index()
- Method: new()
- Method: create()
- Method: edit($id)
- Method: update($id)
- PUT vs PATCH
- Important about Security
- Common Errors
- Good Naming Practice
- ️ Pro Tip: Route Debugging
- ➡️ External Redirects
- Conclusion
CodeIgniter 4, as the modern framework it is, has developed a complete system to handle routes, something that has been present for a while in other frameworks such as Laravel or Django, in which we have an additional layer or level to manage routes; therefore, we have a mechanism to indicate which controller function will process which URL through routes.
In the previous entry, we saw how to delete records in CodeIgniter 4 and worked with controllers and functions, in addition to creating our first controller and associated function and a GET type route.
The Importance of the Controller
Remember that CodeIgniter is an MVC (Model-View-Controller) framework:
- View: The layer the end user sees.
- Model: Responsible for manipulating data and performing CRUD operations with the database.
- Controller: It is the intermediary; it communicates the view with the model and manages the business logic.
- Routes: In CodeIgniter 4, they are mandatory and define how we access the controller's resources.
Composition of Routes
As with most frameworks that work with a routing layer, they basically define 3 basic parameters:
- The URI
- The Controller
- The function within the controller, which is responsible for performing the process
In addition to the type of route, which we discuss a bit further down, which for this example is a POST type:
$routes->post('/save_form', 'Admin::save');Route Types
In CodeIgniter 4, we have multiple types of routes that we can use depending on the method to be employed, that is, if we use GET type routes, it means they will only be able to resolve GET type requests; now, if we are interested in sending a POST, which we generally use for instance to save data from a form, we have to create a POST type route:
Defining Routes in Config/Routes.php
Routes are defined in the app/Config/Routes.php file. Before starting, make sure you have the line $routes->setAutoRoute(false); commented out if you want to define your routes manually and have total control over them.
$routes->post('/save_form', 'Admin::save');Therefore, if we try to access this POST type route via a browser query, it will give us the following error:

Simply a 404 because we are accessing via a GET request a method that is only configured to be accessed via POST.
So, we have to define another route as GET, or simply add another route for the GET:
$routes->get('/', 'Pelicula::index');
$routes->get('/save_form', 'Admin::save');
$routes->post('/save_form', 'Admin::save');Creating a Controller and the use of Namespaces
For a route to work, we need a controller. We will create one called Pelicula.php in the app/Controllers folder:
<?php
namespace App\Controllers;
class Pelicula extends BaseController {
public function index() {
return "Hello World";
}
}And with this, for the following function:
public function save(){ return "save"; }What is a Namespace?
The Namespace is a mechanism for grouping classes into folders. It is essential for the framework to be able to import the class automatically. It must always follow the folder structure: App\Controllers. If you change this path or the class name (for example, if the file is named Peliculas but the class is Pelicula), you will get a 404 error or a loading error.
We have the expected result:

Because the previous route works to be consumed by both GET and POST.
Of course, we also have routes for PUT, PATCH or DELETE types:
$routes->get('products', 'Product::feature'); $routes->post('products', 'Product::feature'); $routes->put('products/(:num)', 'Product::feature'); $routes->patch('products/(:num)', 'Product::feature'); $routes->delete('products/(:num)', 'Product::feature');Which we can use depending on the type of operation we want to perform.
Grouping Routes
In case we have several routes with part of the same URI being identical; for example, let's suppose our route now has the word "admin" in it:
$routes->group('admin', function ($routes) {
$routes->get('save_form', 'Admin::save');
$routes->post('save_form', 'Admin::save');
});Now we have to add an "admin" to the path; in my case:
http://testcodeigniter4.test/admin/save_form

You can add as many grouping levels as you want or need:
$routes->group('admin', function ($routes) {
$routes->group('user', function ($routes) {
$routes->get('save_form', 'Admin::save');
$routes->post('save_form', 'Admin::save');
});
});And to access it:
http://testcodeigniter4.test/admin/user/save_form
And if we want to create another route:
$routes->group('admin', function ($routes) {
$routes->group('user', function ($routes) {
$routes->get('save_form', 'Admin::save');
$routes->post('save_form', 'Admin::save');
$routes->get('index', 'Admin::index'); });
});We can perfectly do it:
http://testcodeigniter4.test/admin/user/index
And with the following function in our controller:
public function index() { return "Admin"; }We have the following:

Parameters for Routes and URLs
We can also define parameters for routes to pass additional data; for example, a number:
$routes->get('save_form/(:num)', 'Admin::save/$1', ['as' => 'admin_save']);From the function:
public function save($id) { return "save: ".$id; }Placeholders
We can configure different placeholders according to the type of data we want to receive:
| Placeholders | Description |
|---|---|
| (:any) | Will match any character passed through the URL. |
| (:segment) | Same as the previous one excluding the /. |
| (:num) | Matches any integer. |
| (:alpha) | Matches any character of the alphabet. |
| (:alphanum) | Matches any character of the alphabet or number. |
| (:hash) |
Named Routes
We can also give a name to our route; later on, in another entry, we will see a practical example of all this, but suppose it is simply a reference that we give to be able to refer to the route by name and NOT directly place the URI; the advantage of this explains itself and in this way, we can perfectly vary the URI without needing to update the reference in our code:
$routes->get('save_form', 'Admin::save', ['as' => 'admin_save']);And from a view; we simply place the reference to the name:
<?= route_to('admin_save') ?>If it were to receive arguments:
<?= route_to('admin_save', $movie->id) ?>Defining our routes for a CRUD operation
The most common thing in systems is that we need to perform CRUD operations, that is, to create, read, update and delete an entity; as you can see, for example for the create and update view we need to define two POST routes for each of these operations, one to show the form and another to process the information, plus the view for the listing would be a total of 7 different routes to perform a generic operation like doing a CRUD; luckily in modern frameworks like CodeIgniter 4, we have a method that allows us to define through a single function or method all types of routes we need:
$routes->resource('photos', $options);You can see the routes we have to specify in the official documentation.
Common Routes
In CI4, we can say that the common routes to use in any application are:
$routes->get('pelicula','Pelicula::index'); // GET type to generate a list of elements
$routes->get('pelicula/new','Pelicula::new'); // GET type to render the creation form
$routes->post('pelicula','Pelicula::create');// POST type to process the form when creating a resource
$routes->get('pelicula/xx/edit','Pelicula::edit');// GET type to render the form to edit an element
$routes->put('pelicula/xx','Pelicula::update');// PUT type to process the entire form of an existing record
$routes->delete('pelicula/xx','Pelicula::delete'); // DELETE type which, surprise, allows deleting a recordThe previous scheme are the URIs used par excellence to perform each of the indicated operations and the common route types to perform the famous CRUDs at the application level.
View Generated Routes
To be able to see the routes generated at the application level, we have the spark command:
php spark routesGenerate Routes Automatically
A very interesting feature of CodeIgniter is the ability to generate routes automatically, according to the methods we have registered; for this, in our routes file, we have to enable it:
$routes->setAutoRoute(true);Given the following controller:
<?php
namespace App\Controllers;
class Pelicula extends BaseController {
public function index()
{
echo "Hello World";
}
public function test($x = 0,$n = 10)
{
echo "Hello World test ".$x." ".$n;
}
// public function new()
// {
// echo view("pelicula/create");
// }
// public function create()
// {
// echo "Processing Form!";
// }
public function edit($id)
{
echo view("pelicula/edit");
}
public function update($id)
{
echo "Processing Form! ".$id;
}
}And you remove the previously defined routes and check which routes have been generated:
+--------+-----------------------+------------------------------------------+
| Method | Route | Handler |
+--------+-----------------------+------------------------------------------+
| auto | / | \App\Controllers\Home::index |
| auto | home | \App\Controllers\Home::index |
| auto | home/index[/...] | \App\Controllers\Home::index |
| auto | pelicula | \App\Controllers\Pelicula::index |
| auto | pelicula/index[/...] | \App\Controllers\Pelicula::index |
| auto | pelicula/test[/...] | \App\Controllers\Pelicula::test |
| auto | pelicula/edit[/...] | \App\Controllers\Pelicula::edit |
| auto | pelicula/update[/...] | \App\Controllers\Pelicula::update |
+--------+-----------------------+------------------------------------------+It is a particularly useful option when you have an application where you can take advantage of this type of feature.
Recommended CRUD Organization
A common organization would be:
GET /peliculaMethod: index()
Show create form
GET /pelicula/newMethod: new()
Create resource
POST /pelicula
Method: create()
Show edit form
GET /pelicula/edit/{id}Method: edit($id)
Update
PUT /pelicula/{id}Method: update($id)
Delete
DELETE /pelicula/{id}Method: delete($id) or destroy($id)
PUT vs PATCH
- PUT → Updates the entire entity.
- PATCH → Partially updates the entity.
Example:
- If you edit the entire post → PUT
- If you only change the title → PATCH
Summary:
| HTTP Method | Suggested Route | Function | Purpose |
|---|---|---|---|
| GET | pelicula | index | List of all movies. |
| GET | pelicula/new | new | Shows the form to create a new movie. |
| POST | pelicula | create | Processes and saves the new movie. |
| GET | pelicula/edit/(:num) | edit | Shows the form to edit a specific movie. |
| PUT/PATCH | pelicula/(:num) | update | Processes the update of data. |
| DELETE | pelicula/(:num) | delete | Deletes the resource from the database. |
Important about Security
Data sent by:
- GET → Travels in the URL.
- POST → Is encapsulated in the request.
Common Errors
A very common one:
- File is named Pelicula.php
- Class is named Peliculas
That generates an error.
Names must match exactly.
That is why POST is safer for sending forms.
Good Naming Practice
Although you can invent names, it is not recommended.
Using conventions like:
- index
- create
- new
- edit
- update
- delete
Allows any developer to quickly understand what the application does without having to do debugging.
️ Pro Tip: Route Debugging
If your route ever doesn't respond as expected, don't guess. Always use the terminal in the root of your project to verify which routes the framework recognizes:
php spark routesThis will show you a clear table with the method, the URI, the handler, and any applied constraints. If your route does not appear there, it does not exist for CodeIgniter.
➡️ External Redirects
Sometimes, it is necessary to redirect a route to an external URL. You can easily do this in your Config/Routes.php file:
$routes->addRedirect('google', 'https://google.com');This is extremely useful for handling obsolete links or delegating processes to third-party services without leaving your route structure.
Conclusion
This is a very good organization for working with routes in CodeIgniter 4.
Can it change? Yes.
But this is the most used scheme today and the one most followed by convention.
The important thing is:
- Understand what each type of request does.
- Be consistent with names.
- Do not reinvent the wheel.
- Maintain clarity in the structure.
With this, you already have a solid reinforcement of the use of routes.
The next step consists of learning how to create a Rest API in CodeIgniter 4.