Content Index
- The Importance of the Controller
- Route Composition
- Route Types
- Defining Routes in app/Config/Routes.php
- Creating a Controller and Using Namespaces
- Grouping Routes
- Parameters in Routes and URLs
- Placeholders
- Named Routes
- Defining Routes for a Complete CRUD
- Common CRUD Routes
- Viewing Generated Routes
- Automatically Generating Routes
- Recommended CRUD Organization
- Method: index()
- Method: new()
- Method: create()
- Method: edit($id)
- Method: update($id)
- PUT vs PATCH
- Important on Security
- Common Errors When Defining Routes
- Best Practices in Function Naming
- ️ Pro Tip: Route Debugging
- ➡️ External Redirects
- Conclusion
CodeIgniter 4, as the modern framework that it is, has a complete system for handling routes, something that has been present for a while in other frameworks like Laravel or Django. In these, there is an additional routing layer that acts as an intermediary between system URLs and controllers, allowing us to precisely define which controller function will process each URL.
In the previous entry we saw how to delete records in CodeIgniter 4, where we worked with controllers and their functions, created our first controller, and defined a GET type route.
The Importance of the Controller
Remember that CodeIgniter 4 is an MVC (Model-View-Controller) framework, and each layer has a well-defined responsibility:
- View: The layer seen by the end user; it handles presentation.
- Model: Handles data manipulation and performs CRUD operations with the database.
- Controller: It is the intermediary; it connects the view with the model and manages business logic.
- Routes: In CodeIgniter 4, routes are mandatory (when auto-routing is disabled) and define how we access controller resources from a URL.
Route Composition
Just like in most frameworks that implement a routing layer, a basic route requires three essential parameters:
- The URI (the URL to be mapped)
- The controller that will handle the request
- The function within the controller that will execute the process
In addition to these three parameters, we must define the route type (or HTTP method), which we discuss in more detail below. For example, the following is a POST type route:
$routes->post('/save_form', 'Admin::save');Route Types
In CodeIgniter 4 we have multiple route types available depending on the HTTP method we need. If we define a GET type route, it will only resolve requests of that type. Similarly, if we want to process data sent from a form, we need a POST route. Choosing the correct method is not only a best practice: it is also a security measure, as it prevents a URL from accepting requests it shouldn't.
Defining Routes in app/Config/Routes.php
Routes are defined in the app/Config/Routes.php file. Before starting, make sure to set $routes->setAutoRoute(false); if you want to define your routes manually and have total control over them. This way, only the routes you explicitly declare will be accessible.
$routes->post('/save_form', 'Admin::save');Therefore, if we try to access this POST type route via a browser request (which defaults to GET), we will get the following error:

A simple 404, because we are accessing via a GET request a method that is only configured to be consumed via POST.
The solution is simple: define an additional GET type route for that same URI:
$routes->get('/', 'Pelicula::index');
$routes->get('/save_form', 'Admin::save');
$routes->post('/save_form', 'Admin::save');Creating a Controller and Using Namespaces
For a route to work, we need a backing 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 "Hola Mundo";
}
}And we add a save() function to handle saving:
public function save() { return "save"; }What is a Namespace?
Thenamespaceis a mechanism to group classes under a logical path that matches the project's folder structure. It is essential for the framework to be able to autoload the class correctly. It must always follow the structure:App\Controllers. If the file name isPeliculas.phpbut the class is namedPelicula, you will get a404error or a class loading error. The file and the class must have the exact same name.
With everything configured correctly, we will have the expected result:

The previous route works to be consumed by both GET and POST, since we defined both pointing to the same controller and function.
Of course, we also have the PUT, PATCH, and DELETE methods:
$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');Each of these methods responds to a different type of operation, and using them correctly is the basis for building a semantically correct API or CRUD.
Grouping Routes
When several routes share a common segment in the URI —for example, the admin prefix—, CodeIgniter 4 allows us to group them with the $routes->group() method to avoid repeating that segment in each definition:
$routes->group('admin', function ($routes) {
$routes->get('save_form', 'Admin::save');
$routes->post('save_form', 'Admin::save');
});With this, the full URI to access those routes becomes:
http://testcodeigniter4.test/admin/save_form

Additionally, you can nest groups to create deeper structures with no level limits:
$routes->group('admin', function ($routes) {
$routes->group('user', function ($routes) {
$routes->get('save_form', 'Admin::save');
$routes->post('save_form', 'Admin::save');
});
});The resulting URI would be:
http://testcodeigniter4.test/admin/user/save_form
And if you need to add more routes within the same group, you simply declare them at the same level:
$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');
});
});Making it accessible at:
http://testcodeigniter4.test/admin/user/index
With the following function in the controller:
public function index() { return "Admin"; }We will have the expected result:

Parameters in Routes and URLs
We can also define parameters within routes to pass dynamic data directly from the URL. For example, to receive an ID number:
$routes->get('save_form/(:num)', 'Admin::save/$1', ['as' => 'admin_save']);The captured parameter ($1) is automatically passed as an argument to the controller function:
public function save($id) { return "save: " . $id; }Placeholders
Placeholders allow us to restrict or validate the type of data expected in a URL segment. CodeIgniter 4 offers the following natively:
| Placeholder | Description |
|---|---|
(:any) | Matches any character passed through the URL, including /. |
(:segment) | Same as (:any), but excludes the / character. |
(:num) | Matches integer numbers only. |
(:alpha) | Matches any character of the alphabet (no numbers or symbols). |
(:alphanum) | Matches any combination of letters and numbers. |
(:hash) | Similar to (:segment), but encodes the value internally. Useful for obfuscated IDs. |
Named Routes
We can assign a name to any route using the 'as' option. This allows us to reference it by its name instead of writing the URI directly. The advantage is clear: if at any point you need to change the URI, you only do it in the route definition and the rest of the code updates automatically:
$routes->get('save_form', 'Admin::save', ['as' => 'admin_save']);From a view, we use the route_to() helper function to generate the URL from the name:
<?= route_to('admin_save') ?>If the route receives arguments, we pass them as additional parameters:
<?= route_to('admin_save', $movie->id) ?>Defining Routes for a Complete CRUD
The most common thing in any application is needing CRUD operations: create, read, update, and delete resources. To implement this manually, you would need at least 7 different routes: one for the listing, one to show the creation form, another to process that form, another for the edit form, another to process it, one to delete, and optionally one to show details. Fortunately, CodeIgniter 4 offers the $routes->resource() method, which generates all those routes with a single line:
$routes->resource('photos', $options);You can check the routes it generates in detail in the official documentation.
Common CRUD Routes
In CodeIgniter 4, the most used routes for any CRUD operation are the following:
$routes->get('pelicula', 'Pelicula::index'); // Item listing
$routes->get('pelicula/new', 'Pelicula::new'); // Creation form
$routes->post('pelicula', 'Pelicula::create'); // Processes and saves the new record
$routes->get('pelicula/(:num)/edit', 'Pelicula::edit'); // Edit form
$routes->put('pelicula/(:num)', 'Pelicula::update'); // Processes the update
$routes->delete('pelicula/(:num)', 'Pelicula::delete'); // Deletes the recordThis scheme represents the URIs and HTTP methods used by convention to perform CRUD operations at the web application level.
Viewing Generated Routes
To inspect at any time what routes your application has registered, CodeIgniter 4 includes the spark command:
php spark routesThis command prints a table in the console with the HTTP method, URI, and associated handler. If a route does not appear in that table, it does not exist for the framework.
Automatically Generating Routes
An interesting feature of CodeIgniter 4 is the ability to automatically generate routes from the public methods registered in your controller. To enable it, you must turn on the option in your routes file:
$routes->setAutoRoute(true);Given the following example controller:
<?php
namespace App\Controllers;
class Pelicula extends BaseController {
public function index()
{
echo "Hola Mundo";
}
public function test($x = 0, $n = 10)
{
echo "Hola Mundo test " . $x . " " . $n;
}
// public function new()
// {
// echo view("pelicula/create");
// }
// public function create()
// {
// echo "Procesando el Form!";
// }
public function edit($id)
{
echo view("pelicula/edit");
}
public function update($id)
{
echo "Procesando el Form! " . $id;
}
}By removing manually defined routes and running php spark routes, you will see that the framework has automatically generated the following:
+--------+-----------------------+------------------------------------------+
| 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 |
+--------+-----------------------+------------------------------------------+This is a particularly useful option in small projects or early development phases, where speed matters more than exhaustive route control. However, for production applications it is recommended to define routes manually for greater security and clarity.
Recommended CRUD Organization
The structure most commonly used by convention in CodeIgniter 4 for a complete CRUD would be the following:
GET /peliculaMethod: index()
Displays the general listing of records.
GET /pelicula/newMethod: new()
Displays the form to create a new resource.
POST /pelicula
Method: create()
Processes the form and saves the new record in the database.
GET /pelicula/edit/{id}Method: edit($id)
Displays the edit form pre-filled with the data of the corresponding record.
PUT /pelicula/{id}Method: update($id)
Processes the form and updates the existing record.
DELETE /pelicula/{id}Method: delete($id) or destroy($id) — Deletes the record from the database.
PUT vs PATCH
PUT→ Replaces the entire entity with the sent data.PATCH→ Updates partially the entity; only the sent fields.
Practical example:
- If the user edits a complete post →
PUT - If only the title changes →
PATCH
Summary of CRUD routes and methods:
| HTTP Method | Suggested Route | Function | Purpose |
|---|---|---|---|
GET | pelicula | index() | Listing of all movies. |
GET | pelicula/new | new() | Displays the form to create a new movie. |
POST | pelicula | create() | Processes and saves the new movie. |
GET | pelicula/edit/(:num) | edit($id) | Displays the form to edit a specific movie. |
PUT / PATCH | pelicula/(:num) | update($id) | Processes the data update. |
DELETE | pelicula/(:num) | delete($id) | Deletes the resource from the database. |
Important on Security
Sent data behaves very differently depending on the HTTP method used:
GET→ Data travels in the URL and is visible in the browser bar, history, and server logs. Never useGETto send sensitive information.POST→ Data is encapsulated in the request body, making it safer for submitting forms with sensitive information.
Common Errors When Defining Routes
One of the most frequent errors when starting with CodeIgniter 4 is the mismatch between the controller file name and the class name:
- File:
Pelicula.php - Class defined as:
class Peliculas
This discrepancy causes a class loading error and returns a 404. The file name and the class name must match exactly, respecting uppercase and lowercase letters.
Best Practices in Function Naming
Although technically you can give any name to your controller functions, following established conventions is an indispensable practice in collaborative projects. The standard names are:
indexcreateneweditupdatedelete
Following these conventions allows any developer to understand at a glance what each function does, without needing to read the code or debug.
️ Pro Tip: Route Debugging
If your route ever doesn't respond as expected, don't guess. Always run the following command from your project root to check which routes the framework recognizes:
php spark routesThis command prints a clear table with the HTTP method, URI, handler, and any applied restrictions. If your route does not appear in that table, it does not exist for CodeIgniter 4.
➡️ External Redirects
Sometimes it is necessary to redirect a route to an external URL. You can easily do this within app/Config/Routes.php using the addRedirect() method:
$routes->addRedirect('google', 'https://google.com');This is extremely useful for handling outdated URLs, doing route migrations without breaking existing links, or delegating processes to third-party services without leaving your application's routing structure.
Conclusion
This is a very solid and proven organization for working with routes in CodeIgniter 4. It may vary depending on each project's needs, but it is the most used scheme nowadays and the one most followed by convention in the community.
The important thing is:
- Understand what each type of HTTP request does and when to use it.
- Be consistent with function names and URIs.
- Don't reinvent the wheel; conventions exist for a reason.
- Maintain clarity in the structure to facilitate project maintenance.
With this you already have a solid mastery of CodeIgniter 4's routing system. The next step is to learn how to create a REST API in CodeIgniter 4.