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

Routing Laravel Package

marko/routing

Routing component for the Marko PHP framework. Register routes, map URLs to controllers, and generate links with a clean, Laravel-inspired API. Supports route groups, parameters, middleware, and named routes to keep application URLs organized and maintainable.

View on GitHub
Deep Wiki
Context7

Getting Started

marko/routing is a component package, not a standalone library — it's the routing layer of the Marko framework and is pulled in transitively when you install the framework itself. The repository is marked [READ-ONLY], so there is no separate versioning or issue tracker to follow; everything lives in the main marko-php/marko monorepo.

Minimal path to a first request:

  1. Install the framework (composer require marko/framework or scaffold via the Marko installer), which brings marko/routing along with it.

  2. Open the route definition file your skeleton provides — typically routes/web.php (and often a sibling routes/api.php). This is the first place to look: the routing package registers a Route facade/helper bound in the container, and the routes file is just executed at boot.

  3. Define your first route with a closure:

    Route::get('/hello/{name}', function ($name) {
        return "Hello, {$name}";
    });
    
  4. Hit it via your dev server / front controller and confirm the parameter is injected.

That's the whole first-use case: a parameterized GET route backed by a closure. Everything else (controllers, middleware, groups) is layering on top of that primitive. Until you understand that routes resolve to a handler plus a middleware stack, the rest of the package looks more magical than it is.

Implementation Patterns

The package is Laravel-shaped — the day-to-day API will feel familiar if you've used Illuminate\Routing, and the patterns below are the ones you'll actually reach for.

Controllers over closures. Closures are fine for smoke tests but don't cache well and don't get resolved through the container consistently. Point routes at [Controller::class, 'method'] or the string form 'Controller@method' so dependency injection and middleware can kick in.

Verbs and convenience methods. Route::get/post/put/patch/delete cover most cases. For anything spanning multiple verbs, Route::match([...], ...) and Route::any(...) exist. Don't reach for any reflexively — it makes 405s impossible and complicates route caching.

Parameters and constraints. {id} is required, {id?} is optional (must be trailing). Add regex constraints via ->where('id', '[0-9]+') or ->whereNumber('id') if the shorthand exists in your version. Constraints are the right place to reject bad input — don't validate numeric IDs in the controller if the router can do it for free.

Groups for prefix/middleware/namespace. This is where the package earns its keep:

Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'admin']], function () {
    Route::get('/users', [UserController::class, 'index']);
});

Prefer groups over repeating prefixes and middleware strings on every route — the group closure is evaluated once at boot, and changing the prefix is a one-line edit.

Named routes and URL generation. Name routes (->name('users.show')) and generate URLs with route('users.show', ['user' => $id]) rather than hardcoding paths. This is the single biggest long-term win: renaming a URI becomes a non-event. It also pairs with redirects (redirect()->route(...)).

Middleware as the integration seam. Routing is where your middleware pipeline is assembled. Anything cross-cutting — auth, throttling, CORS, request logging — belongs on route middleware, not in controllers. Order matters: middleware runs in the order declared, and route middleware runs after global middleware.

Model binding. If route parameters are named to match a model (e.g. {user}), the package may resolve a model instance automatically. This is worth using for the "clean controller" feel, but be aware of the resolution semantics — implicit binding typically hits findOrFail and 404s on miss, which is usually what you want but occasionally isn't.

API vs web split. The web and api route files usually differ in middleware (web gets sessions/CSRF; api gets a stateless prefix like /api). If you're exposing a JSON API, define routes in the api file and skip session middleware for performance.

Gotchas and Tips

  • Read-only repo. All issues and PRs go to marko-php/marko. Don't open anything against this repo — it will be ignored. When filing a bug, check the monorepo's issue tracker and reference marko/routing in the title.
  • Low adoption, sparse docs. With 0 dependents and a very small community, you won't find much StackOverflow coverage. Your fastest debugging loop is reading the package source directly (vendor/marko/routing/src) — it's small enough to read in an afternoon, and reading it will answer almost every ambiguity faster than searching.
  • Definition order matters. Route matching is first-match-wins in the underlying dispatcher (typically FastRoute). A catch-all like /{slug} defined before /about will swallow /about. Always define specific routes before dynamic ones.
  • Trailing slashes are not usually equivalent. /users and /users/ are distinct patterns. Pick a convention and normalize at the web-server layer (nginx try_files) or accept that users will occasionally 404 on a trailing slash.
  • Method spoofing. HTML forms only do GET/POST. If you're using PUT/DELETE routes, confirm the framework's method-override middleware is enabled and that your form posts _method. A 405 on a form submission is almost always this.
  • Route caching in production. Many Laravel-style routers support route:cache. If Marko offers an equivalent, use it — but remember the cache is invalidated only by your cache-clear command, not by editing the routes file. New routes returning 404 in prod but working locally is a cache-staleness symptom.
  • Middleware order is load-bearing. Auth before throttle vs throttle before auth changes behavior under load. Also, if a middleware short-circuits (returns a response without $next($request)), downstream middleware and the controller never run — this is the mechanism, not a bug, but it surprises people debugging "why isn't my controller hit."
  • 405 vs 404 confusion. By default, routers often return 404 for a known path with the wrong verb unless the dispatcher is configured for strict method handling. If you want 405s, verify the dispatcher's configuration rather than fighting it in controllers.
  • Container resolution timing. Controller-string routes resolve through the container at dispatch time; closure routes run inline. If you're relying on constructor injection in a controller, use controller routes — closures won't get it.
  • Extension points. If you need custom route behavior, subclass the Router/Route bindings in the container rather than patching vendor files. The facade just resolves a binding — rebinding is the supported override mechanism.
  • Pin to framework version. Because this ships with the framework and is read-only here, version skew between marko/framework and a manually-required marko/routing is a real risk. Don't require it directly; let the framework manage the constraint.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core