Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Router Laravel Package

aura/router

Aura\Router is a fast, standalone PHP routing library with PSR-7 support, named routes, route groups, and flexible matching for web apps and APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and build a container once per request:

composer require aura/router
use Aura\Router\RouterContainer;
use Laminas\Diactoros\ServerRequestFactory;

$routerContainer = new RouterContainer();
$map = $routerContainer->getMap();

$map->get('home', '/', fn ($request) => 'home');

$map->get('post.show', '/posts/{id}', fn ($request) => 'post ' . $request->getAttribute('id'))
    ->tokens(['id' => '\d+']);

$request = ServerRequestFactory::fromGlobals();
$matcher = $routerContainer->getMatcher();

if ($route = $matcher->match($request)) {
    // $route->handler, $route->params, $route->attributes
} else {
    $failed = $matcher->getFailedRoute(); // ->failedRule, ->allows
}

Where to look first, in this order:

  1. Aura\Router\RouterContainer — the only object you need; it hands out the map, matcher, and generator.
  2. Aura\Router\Map — get(), post(), route(), attach().
  3. Aura\Router\Route — tokens(), defaults(), host(), secure(), accepts(), allows().
  4. Aura\Router\Matcher — match() and getFailedRoute(); this is where your debugging starts.
  5. Aura\Router\Generator — generate() for URL building.

First use case: a small standalone PSR-7 app, or a framework-agnostic package you want to work in any PSR-7 stack. The typical bootstrap is: route definitions in one file, ServerRequestFactory::fromGlobals() once, match() once, then hand $route + $request to your dispatcher. There is no framework magic — everything is explicit, which makes it very testable.

Implementation Patterns

Named routes and URL generation — direct analogue of Laravel's route():

$generator = $routerContainer->getGenerator();
echo $generator->generate('post.show', ['id' => 42]); // /posts/42

Multi-method routes — like Laravel's Route::match():

$map->route('thing', '/things/{id}')
    ->allows(['GET', 'PATCH', 'DELETE']);

Groups — like Route::prefix()->name()->group(); the callback receives a group object with the same registration API:

$map->attach('admin.', '/admin', function ($group) {
    $group->tokens(['id' => '\d+']);       // applies to every route inside
    $group->defaults(['area' => 'admin']); // merged into each route's defaults
    $group->get('users', '/users', ...);   // name: admin.users, path: /admin/users
});

Constraints and metadata — ->tokens([...]) is the ->where() equivalent; ->defaults([...]) attaches route metadata (controller/action/area) that your dispatcher can read off $route->attributes.

Request constraints beyond the path — this is where Aura earns its keep over FastRoute:

$map->get('api.things', '/things')
    ->host('api.example.com')
    ->secure()
    ->accepts(['application/json']);

Dispatching — Aura.Router never resolves handlers. Keep your own wiring or use a container:

$route = $matcher->match($request);
if ($route) {
    $request = $request->withAttributes(
        array_merge($request->getAttributes(), $route->attributes, $route->params)
    );
    $response = $container->call($route->handler, ['request' => $request]);
}

Proper 405s, which many home-grown routers skip:

if ($failed && $failed->failedRule === \Aura\Router\Route::FAILED_METHOD) {
    return new TextResponse('Method Not Allowed', 405, [
        'Allow' => implode(', ', $failed->allows),
    ]);
}

Use Route::FAILED_ACCEPTS for 406, Route::FAILED_AUTH for 401, Route::FAILED_SECURE for upgrade-to-HTTPS redirects.

Embedding in a Laravel app — don't fight Laravel's router. Register one catch-all and delegate a sub-tree:

// routes/web.php
Route::any('/legacy/{path?}', function (Illuminate\Http\Request $request) use ($routerContainer) {
    $psrRequest = (new Psr17Factory())->createServerRequest(
        $request->method(), $request->getRequestUri()
    );
    // match, dispatch, convert PSR-7 response back to a Laravel Response
})->where('path', '.*');

Testing — the biggest practical win: no globals, no container boot. Build a RouterContainer, construct a ServerRequest with an explicit method/URI/Accept header, assert on match() and getFailedRoute(). This makes a PSR-7 router far easier to unit-test than a global Laravel route collection.

Registering routes from a config array — for larger apps, iterate a PHP config and call $map->route($name, $path, $handler)->tokens(...); this is the closest thing you get to a route registry.

Gotchas and Tips

  • No middleware, no controller resolution, no container. Handlers are opaque values. You must dispatch. Pair with Aura\Dispatcher or your own PSR-11 container or you'll get stuck immediately.
  • Aura.Router ≠ Laravel routes. No model binding, no {id} implicit DI, no route caching, no session/CSRF/auth middleware, no php artisan route:list. Treat it as a matching engine, not a request lifecycle.
  • Order matters for overlapping paths. The first route that passes all rules wins. Define specific routes before catch-alls.
  • Trailing slashes are exact. /foo does not match /foo/. Normalize in the request factory or register both.
  • accepts() has a soft default. With no Accept header the rule usually passes as */*; test explicitly with and without the header before relying on it for API versioning.
  • secure() behind a proxy. The check reads the request URI scheme. If you're behind a load balancer, make sure ServerRequestFactory::fromGlobals() (or your PSR-7 factory) reflects the forwarded scheme, or you'll get phantom failures in production only.
  • defaults() is not Laravel's ->defaults(). It's per-route metadata merged into $route->attributes; nothing is auto-applied to a handler.
  • HEAD and OPTIONS are not automatic. Define them explicitly, or intercept CORS preflight in a middleware layer before the matcher runs.
  • No route caching. The map is rebuilt every request unless you cache your own bootstrap. For large route tables, cache a compiled config file rather than the Map object.
  • Duplicate route names throw. Group name prefixes use a trailing dot (admin.), so keep naming disciplined; there's no "last one wins" like some routers.
  • Debugging is good but niche. $matcher->getFailedRoute()->failedRule tells you which rule killed the match (FAILED_METHOD, FAILED_ACCEPTS, FAILED_HOST, FAILED_TOKEN, FAILED_SECURE, FAILED_NOT_FOUND). Inspect the matcher's log (if present in your version) to trace every attempt.
  • Version pinning matters. v4 is PSR-7/PHP 8+; older v3 used Aura\Web\Request and will not work with the examples above. Verify what Composer pulls in.
  • Small community footprint. 500 stars means Stack Overflow is thin. The upside: the source is tiny — read Matcher, Rule\*, and Generator directly; you'll understand the whole package in an afternoon.
  • Extension points are pragmatic, not formal. Subclass RouterContainer, wrap the matching step in a PSR-15 middleware for cross-cutting concerns, and keep custom behaviour in your dispatcher rather than reaching into rule internals.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect