Routes have evolved quite a bit in CodeIgniter 4, taking hints from a number of other frameworks. I won’t cover all of the possibilities today because there’s a lot. The User Guide has you covered there. Instead, let’s start at the most basic way to display a page and go from there.

Closure Routes

You can use simple functions in your routes file now to display a page, perform a small action, etc. This is done using a closure, or anonymous function, and looks something like this:

$routes->add('dashboard', function()
{
    echo view('dashboard');
});

This route would accept any request at http://yoursite.com/dashboard, and serve up the view file, dashboard. These are great for small things, but should be used with care as many things are better handled elsewhere. Good uses might be Privacy Policy pages, sitemaps, etc. Anything that requires any logic, or more than a line or two to handle is best handled within a controller.

Controller-based Routes

CodeIgniter 3 style auto-routing based on the URI is still supported, but we’re not covering that today. Just keep it in the back of your mind that you can still do that. Personally, I recommend turning that option off, though,
and specifying every route in your application. This ensures that no unintended routes are accidentally exposed, it acts as a form of documentation for your app, and it has a whole lot more flexibility and power than the old way.

If we need to pull a lot of stats into our dashboard, we would move that logic to a new controller, say application/Controllers/DashboardController.php. Then we would call the route like so:

$routes->add('dashboard', 'DashboardController::index');

This tells CodeIgniter to accept any request at /dashboard, once again. This time, though, it says to use the DashboardController class, and run the index method. In order to locate the class it needs to know the namespace
of the class. We didn’t tell it specifically, so how does it know? In application/Config/Routes.php there is an option where you can specify what namespace to use when the route doesn’t specify one:

$routes->setDefaultNamespace('AppControllers');

So, our dashboard route will look for the class AppControllersDashboardController. Unless you’ve changed paths,
this expects the file to be at Application/Controllers/DashboardController.php.

URI Parameters

Many routes need dynamic information in the URI, like the ID of a product or user. This is set very similarly to how it was done in previous versions of CodeIgniter, with placeholders:

$routes->add('products/(:segment)', 'ProductController::show/$1');

The (:segment) takes any character within a single URI segment, and it gets passed into the show() method as the first argument, represented by the $1. The method signature might look something like:

public function show(string $productId) { ... }

Multiple placeholders can be passed in any order to the method, or skipped entirely:

$routes->add('category/(:segment)/product/(:num)', 'ProductController::show/$2');

The (:segments) accepts any string as the category id, but then restricts the product id to being a number. In the method we only need the product id, so we only use $2, which represents the second matched placeholder.

HTTP Verbs

So far, we’ve been using $routes->add() to create new routes. That’s not really recommended, though. Instead, you should start using HTTP verbs (get, post, etc) more often in your work. This allows you to specify the exact
HTTP method that route is allowed on, keeping you from accidentally opening up a form or something.

To use the different HTTP methods, you would replace the add() method with the verb name, like get() or post():

$routes->get('product/(:num)', 'ProductController::show/$1');
$routes->post('product/(:num)', 'ProductController::save/$1');

NOTE: nginx does not support all HTTP methods out of the box, from what I understand, but needs to be compiled correctly with WebDAV support to enable that. I could be wrong, but I ran into something similar at work just
today.

Grouping Routes

Finally, one of the last really big features I recommend in the new system are groups. Groups allow you to logically group a set of routes, adding a prefix to the URI in the process. A simple way to setup an admin section:

$routes->group('admin', function($routes) {
    $routes->get('product/(:num)', 'ProductController::show/$1');
    $routes->post('product/(:num)', 'ProductController::save/$1');
});

Now, instead of the product routes being accessed at /product/123, the uri would be /admin/product/123.

It is possible to nest groups, also, if you want to save a little more typing:

$routes->group('admin', function($routes) {
    $routes->get('/', 'DashboardController::index');
   
    $routes->group('products', function($routes) {
        $routes->get('(:num)', 'ProductController::show/$1');
        $routes->post('(:num)', 'ProductController::save/$1');
    });
});

Now the uri’s are still the same (/admin/product/123) but you can keep adding product routes without typing product over and over again.

While that’s convenient, routes also allow you to specify additional options, like modifying the namespace the controllers will use, allowing you to group controllers logically in the filesystem, while still having them
display nicely:

$routes->group('admin', ['namespace' => 'App\Controllers\Admin'], function($routes) {
    $routes->get('product/(:num)', 'ProductController::show/$1');
    $routes->post('product/(:num)', 'ProductController::save/$1');
});

Here we’ve told it to use a new namespace, AppControllersAdmin for all controllers used within the group. This allows your Admin controllers to be logically group into their own Admin directory within the normal Controller’s directory.


This does not touch on every feature of the new routing system by a long shot. You should take some time to read over the docs to see the rest of the features. Hopefully, this gives you a good handle on how to get started using the new routes, though.