Skip to main content

Routing

Basic Routes

// routes/web.php
routerGroup("/", function() {
route(method(GET), url_path("/"), function() {
render(200, html_out(template(app_root() . "/templates/pages/home.php", [])));
});

route(method(GET), url_path("/about"), function() {
render(200, html_out(template(app_root() . "/templates/pages/about.php", [])));
});
});

// routes/api.php
routerGroup("/api", function() {
route(method(GET), url_path("/"), function() {
render(200, json_out(["message" => "Hello World!"]));
});

route(method(POST), url_path("/users"), function() {
$data = json_in();
render(201, json_out(["created" => $data]));
});

route(method(PUT), url_path_params("/users/:id"), function() {
$id = $_GET[':id'];
$data = json_in();
render(200, json_out(["updated" => $id, "data" => $data]));
});

route(method(DELETE), url_path_params("/users/:id"), function() {
$id = $_GET[':id'];
render(204, "");
});
});

Route Files

TeensyPHP projects separate routes into files:

// bootstrap.php (loads route files)
router(function() {
use_request_uri();
require_once app_root() . "/routes/web.php";
require_once app_root() . "/routes/api.php";
});

URL Parameters

// routes/api.php
routerGroup("/api", function() {
// Single parameter: GET /api/users/123
route(method(GET), url_path_params("/users/:id"), function() {
$userId = $_GET[':id'];
render(200, json_out(["user_id" => $userId]));
});

// Multiple parameters: GET /api/orders/from/2024-01-01/to/2024-12-31
route(method(GET), url_path_params("/orders/from/:from_date/to/:to_date"), function() {
$from = $_GET[':from_date'];
$to = $_GET[':to_date'];
render(200, json_out(["from" => $from, "to" => $to]));
});

// Mixed with query strings: GET /api/products/electronics?sort=price
route(method(GET), url_path_params("/products/:category"), function() {
$category = $_GET[':category'];
$sort = $_GET['sort'] ?? 'name';
render(200, json_out(["category" => $category, "sort" => $sort]));
});
});

Action Classes

// App/Actions/Post/ListPosts.php
namespace App\Actions\Post;

use App\Entity\Post;

class ListPosts
{
public function __invoke()
{
$posts = Post::findAll();
$output = array_map(fn($p) => $p->toArray(), $posts);
render(200, json_out(["posts" => $output]));
}
}

// App/Actions/Post/CreatePost.php
namespace App\Actions\Post;

use App\Entity\Post;

class CreatePost
{
public function __invoke()
{
$data = json_in();
$post = Post::create($data);
render(201, json_out(["post" => $post->toArray()]));
}
}

// App/Actions/Post/ShowPost.php
namespace App\Actions\Post;

use App\Entity\Post;
use TeensyPHP\Exceptions\TeensyPHPException;

class ShowPost
{
public function __invoke()
{
$post = Post::find($_GET[':id']);
if (!$post) {
TeensyPHPException::throwNotFound();
}
render(200, json_out(["post" => $post->toArray()]));
}
}
// routes/api.php
use App\Actions\Post\ListPosts;
use App\Actions\Post\CreatePost;
use App\Actions\Post\ShowPost;

routerGroup("/api", function() {
route(method(GET), url_path("/posts"), ListPosts::class);
route(method(POST), url_path("/posts"), CreatePost::class);
route(method(GET), url_path_params("/posts/:id"), ShowPost::class);
});

Web Routes with Templates

// App/Actions/Home/DisplayHome.php
namespace App\Actions\Home;

class DisplayHome
{
public function __invoke()
{
$accept = request_header('Accept');
if ($accept === 'application/json') {
render(200, json_out(['message' => 'Hello World']));
} else {
render(200, html_out(template(app_root() . "/templates/pages/home.php", [])));
}
}
}

// routes/web.php
use App\Actions\Home\DisplayHome;

routerGroup("/", function() {
route(method(GET), url_path("/"), DisplayHome::class);
});

How Routing Works

The route() function matches incoming requests against defined routes. When both the HTTP method and URL path match, the associated action (callback or controller) is executed.

Routes are evaluated in order, and the first match wins. The router stops after finding a match.

Function Reference

route()

route(bool $method, bool $path, callable|string $action): void

Defines a route that matches when both method and path predicates return true.

routerGroup()

routerGroup(string $pathPrefix, callable $action): void

Groups routes under a common URL prefix. Groups can be nested.

method()

method(string $httpMethod): bool

Returns true if the current request matches the specified HTTP method.

url_path()

url_path(string $path): bool

Returns true if the current request URL matches the specified path exactly.

url_path_params()

url_path_params(string $pattern): bool

Returns true if the URL matches the pattern. Captures named parameters (prefixed with :) into $_GET.

app_root()

app_root(): string

Returns the application root directory (defined in globals.php as APP_ROOT).

HTTP Methods

ConstantHTTP MethodTypical Use
GETGETRetrieve resources
POSTPOSTCreate resources
PUTPUTReplace resources
PATCHPATCHPartial updates
DELETEDELETERemove resources
HEADHEADHeaders only
OPTIONSOPTIONSCORS preflight
CONNECTCONNECTTunnel connections
TRACETRACEDebugging