HTTP Request Handling

This guide is meant as a quick primer for those already familiar with PHP frameworks, and want to quickly understand how Apex handles things. Below will explain HTTP controller creation, routes definitions including dynamic path parameters, and brief overview of views via auto-routing. Please refer to the HTTP Request Handling section of the documentation for full details.

If you have not already done so, please create a package by running the following terminal command within the Apex installation directory:

./apex package create demo

Basic Overview

  • Apex makes no assumptions on handling HTTP requests aside from they must be PSR 7 and 15 compliant.
  • Default location of all middleware is the ~/src/HttpControllers directory, although this is not necessarily required.
  • Straight forward router is included, which utilizes the ~/boot/routes.yml file for route definitions, and supports both multi-hosts and dynamic path parameters. Designed with interopability in mind, and with the RouterInterface and RouterResponseInterface interfaces you may rather easily Implement Your Own Router.
  • With default installation, HTTP requests that do not match any route definitions are handled by the PublicSite HTTP controller, which utilizes the Syrus template engine via auto-routing to display views relative to the ~/views/html directory.

For an example of the default auto-routing, run the following terminal commands to create a view:

./apex create view demo characters/fox
echo "<h1>The Quick Brown Fox</h1> <p>You are at /guides/http_requests</p>" > views/html/characters/fox.html

Now open your browser to and you will see the view with the title "The Quick Brown Fox". Alternatively, view a URL for which there is no view within the ~/views/html directory, and the default 404.html view will be displayed. Straight forward enough, and let's move on to HTTP controller creation.

Create HTTP Controller

Create a new HTTP controller with the following command:

./apex create http-controller demo actors --route actor/

A new file is now located at ~/src/HttpControllers/Actors.php which is registered to the demo package and will be included within its repository. Next open the file at ~/boot/routes.yml, and since a --route option was passed in the above command, a new entry has been added within the routes section:

  actor/: Actors
  default: PublicSite

Any HTTP requests that have a URI beginning with /actor/ will be passed to the new Actors controller for processing. For an example to output JSON objects, open the file at ~/src/HttpControllers/Actors.php and replace the process() method with:

public function process(ServerRequestInterface $request, RequestHandlerInterface $app): ResponseInterface

    // Set JSON vars
    $json = json_encode([
        'host' => $app->getHost(), 
        'path' => $app->getPath()

    // Create PSR7 compliant response
    $response = (new Response(body: $json))
        ->withAddedHeader('Content-type', 'application/json');

    // Return
    return $response;

Open your browser to any /actor/ URL such as, and the JSON object will be output to the browser.

Render Template

Instead of returning a JSON object, render the initial view we created with the path variable filled in. Open the ~/src/HttpControllers/Actors.php file again, and replace the process() method with:

public function process(ServerRequestInterface $request, RequestHandlerInterface $app): ResponseInterface

    // Assign /guides/http_requests variable
    $this->view->assign('path', $app->getPath());

    // Render template
    $html = $this->view->render('characters/fox');

    // Create PSR7 compliant response
    $response = new Response(
        body: $html

    // Return
    return $response;

Refresh the page in your browser, and you will now see the view with the title "The Quick Brown Fox" that we previously created displayed, but this time with the path variable filled in.

Full Match Routes

The current route will match any request that has a path beginning with /actor/ meaning /actor/anytihng will trigger the route. You may easily change this to a full match route so only the path /actor will trigger it by simply placing a dollar sign ($) at the end of the route definition. Open the ~/boot/routes.yml file, and modify the rout accordingly:

  actor$: Actors
  default: PublicSite

Refresh your browser again, and since the actor$ route no longer matches the request is passed to the PublicSite HTTP controller, hence the 404 view is displayed. However, if you visit then the route will match and be triggered again.

Dynamic Path Parameters

Create a new view and corresponding route with the command:

./apex create view demo category --route category/:slug/:dealer
echo "<h1>~category~</h1> <p>Welcome to the ~category~ category for ~dealer~.</p>" > views/html/category.html

If you look within the /boot/routes.yml file, you will notice that since the optional --route flag was used a new route was automatically added:

  "category/:slug/:dealer": PublicSite

The above route will treat the second and third segments of the path as dynamic path parameters. Also, if you look in the /etc/Demo/registry.yml file, you will notice it lists both the newly created view and route. This means the view will be included within the package's repository, and the route will be automatically added when the package is installed on other systems.

Since the PublicSite HTTP controller is being used, auto-routing will take effect meaning the new /views/html/category.html template will be rendered. Next, open the /views/php/category.php file and replace it with the following contents:

declare(strict_types = 1);

namespace Views;

use Apex\Svc\{View, App};

 * Render the template.
class category

     * Render
    public function render(View $view, App $app)

        // Get dynamic path params
        $category = $app->pathParam('slug', 'unknown');
        $dealer = $app->pathParam('dealer', 'unknown');

        // Assign variables
        $view->assign('category', ucwords($category));
        $view->assign('dealer', $dealer);


The use declarations at the top were modified to include the App class, and the render() method was filled out to retrieve the values of the dynamic path parameters and assign them as variables within the view. Also please note, injection is performed on the render() method within views, meaning you can place any desired dependencies / container items desired into it.

Open your browser to any URL such as, and it will work exactly as expected. The two dynamic path parameters were correctly extracted, which were retrived via the App::pathParam() method and assigned to the view.

Multi Host Routing

Support for multi host routing is included by simply changing the route definitions to be an array of route definitions, with the top-level item of each array being the host name. For example, within the ~/boot/routes.yml file:

    admin/: WikiAdmin
    default: Wiki

    api/: RestApi
    default: PublicSite

With the above, all HTTP requests to the host will be passed to either the WikiAdmin or Wiki HTTP controllers, and all other HTTP requests will be handled by the other default set of route definitions.

Cleaning Up

You may remove everything created during this guide with the following terminal command:

./apex package delete demo

For more in-depth information regarding this topic, please refer to the HTTP Request Handling section of the documentation. You may also check out the various other available guides or the full developer documentation. If you ever need any assistance with Apex, you can always ask on the /r/apexpl sub Reddit.