PHPackages                             kirago/laravel-routify - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. kirago/laravel-routify

ActiveLibrary[Framework](/categories/framework)

kirago/laravel-routify
======================

Auto-discover and register Laravel route files spread across multiple folders, with configurable glob patterns, middleware groups and a fluent builder.

v1.1.1(2w ago)235—3.4%MITPHPPHP ^8.2CI passing

Since Apr 26Pushed 2w agoCompare

[ Source](https://github.com/kirago-tech/laravel-routify)[ Packagist](https://packagist.org/packages/kirago/laravel-routify)[ Docs](https://github.com/kirago-tech/laravel-routify)[ RSS](/packages/kirago-laravel-routify/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (10)Versions (6)Used By (0)

Laravel Routify
===============

[](#laravel-routify)

[![Packagist Version](https://camo.githubusercontent.com/7ca14a0d108b202d39e0bd369a3fd8eb3449258ad54a0f9eaab6ee4b80a6e244/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6b697261676f2f6c61726176656c2d726f75746966792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kirago/laravel-routify)[![Tests](https://camo.githubusercontent.com/21c52fd7be563a3c830ca6dfe11520e18373f8fa01d2ee62413dc31d415856b0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6b697261676f2d746563682f6c61726176656c2d726f75746966792f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/kirago-tech/laravel-routify/actions/workflows/tests.yml)[![Code Style](https://camo.githubusercontent.com/53ee53e644564c02137adb0f082ca33ffd979798928d5db815359351d05b4809/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6b697261676f2d746563682f6c61726176656c2d726f75746966792f70696e742e796d6c3f6272616e63683d6d61696e266c6162656c3d70696e74267374796c653d666c61742d737175617265)](https://github.com/kirago-tech/laravel-routify/actions/workflows/pint.yml)[![Total Downloads](https://camo.githubusercontent.com/3ced4880d1e2644dbe5cfa19860277ee0f21a9cac0b5cace4b072cad977b6384/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6b697261676f2f6c61726176656c2d726f75746966792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kirago/laravel-routify)[![License](https://camo.githubusercontent.com/3d9ad7fae8ca1cbaf7e7e27242d3e627a5efe1a737988acb58599b2899151153/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6b697261676f2f6c61726176656c2d726f75746966792e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

**Auto-discover and register Laravel route files spread across multiple folders.**Configurable glob patterns, middleware groups, stacks (web / api / console / channels — or your own), opt-in filesystem cache, and a fluent builder for the cases the config can't express.

---

Why this package
----------------

[](#why-this-package)

### The pain: `routes/api.php` rots

[](#the-pain-routesapiphp-rots)

In a fresh Laravel app, every API route lives in `routes/api.php`. That file keeps growing. By the time the app has billing, auth, inventory, users, notifications and a few admin endpoints, it looks like this:

```
// routes/api.php — 400+ lines, nobody wants to touch it
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/users',         [UserController::class, 'index']);
    Route::post('/users',        [UserController::class, 'store']);
    Route::get('/users/{user}',  [UserController::class, 'show']);
    // … 30 more user routes …

    Route::get('/invoices',      [InvoiceController::class, 'index']);
    Route::post('/invoices',     [InvoiceController::class, 'store']);
    // … 25 more billing routes …

    Route::get('/products',      [ProductController::class, 'index']);
    // … 20 more inventory routes …
});
```

Pull-request reviews on this file are painful. Merge conflicts are constant. Finding *"where is the route for X"* requires `grep`. Multiply by `web.php`, `console.php`, `channels.php` and any custom stack (`admin`, `internal`, …) and the boilerplate compounds.

### The disciplined workaround that's still painful

[](#the-disciplined-workaround-thats-still-painful)

The seasoned move is to split per feature and `require` each piece manually from a central group:

```
// routes/api.php
Route::middleware('api')->prefix('api')->group(function () {
    require __DIR__.'/api/billing.php';
    require __DIR__.'/api/users.php';
    require __DIR__.'/api/inventory.php';
    require __DIR__.'/api/auth.php';
    require __DIR__.'/api/notifications.php';
    require __DIR__.'/api/admin.php';
    // adding a new module? don't forget to add it here too…
});
```

Or worse — the same boilerplate inflated inside a `RouteServiceProvider`with five `Route::group(…)->group(__DIR__.'/…')` chains, one per feature folder.

It's better than the giant file, but every new module forces a touch on this central index. **Forget the line and the routes silently disappear in production.** That last part is the killer: there's no compiler error, no test failure unless you remembered to write one — just a 404 on staging at 9 PM the day before launch.

### What Routify does

[](#what-routify-does)

Point Routify at a folder, drop your route files anywhere underneath, stop maintaining the index:

```
// bootstrap/app.php (or any service provider)
use Kirago\Routify\Facades\Routify;

Routify::discover();
```

```
app/Modules/
├── Billing/Routes/api.php          → loaded as api/* with the api stack
├── Billing/Routes/web.php          → loaded as web/* with the web stack
├── Catalog/Routes/api.php
├── Catalog/Routes/api-v2.php       → versioning is just a filename
└── Users/Routes/api.php

```

- Add a module → its routes are picked up. No central file to edit.
- Delete a module → its routes disappear with it. No dangling `require`.
- Version your API by creating `api-v2.php` next to `api.php`. The default glob pattern `api*.php` matches both.
- Need a custom stack (`admin`, `internal`, `webhooks`, `tenant-api`)? Declare it in `config/routify.php` and Routify treats it like a first-class citizen.

The "register every file by hand" tax is gone.

---

Installation
------------

[](#installation)

```
composer require kirago/laravel-routify
```

The package auto-registers via Laravel's package discovery. Publish the config to customise it:

```
php artisan vendor:publish --tag=routify-config
```

Requires PHP 8.2+ and Laravel 11, 12 or 13.

> **Zero-crash install** — the shipped config has `paths => []`. The package is wired but does nothing until you point it at the directories your app uses. No surprise exceptions on first boot.

---

Quick start
-----------

[](#quick-start)

1. Add your route directories to `config/routify.php`:

```
'paths' => [
    app_path('Modules'),
    // app_path('Features'),
    // base_path('packages'),
],
```

2. Drop route files anywhere under those roots:

```
app/Modules/
├── Billing/Routes/api.php       → Route::get('/invoices', …)
├── Billing/Routes/web.php
├── Catalog/Routes/api.php
└── Catalog/Routes/api-v2.php

```

…and they all become `api/invoices`, `api/v2/*` etc. with the `api`middleware group applied. With `auto_discover_on_boot = true` (the default), that's it — no extra wiring.

### Folder-based discovery — drop a file, get a route

[](#folder-based-discovery--drop-a-file-get-a-route)

Since 1.1, you don't have to name your route files `api*.php` or `web*.php`anymore. Drop them in a folder named after the stack and Routify picks them up. The two modes coexist; pick whichever fits.

```
app/Modules/
└── Billing/Routes/
    ├── api.php             ← matched by pattern → api stack (works since 1.0)
    ├── api/
    │   ├── billing.php     ← matched by folder → api stack (new in 1.1)
    │   ├── invoices.php    ← matched by folder → api stack
    │   └── v2/orders.php   ← matched by folder, any depth → api stack
    └── web/
        └── portal.php      ← matched by folder → web stack

```

The rule is: under any `paths[]` directory, a `.php` file whose relative path contains a segment exactly equal to a declared stack key (`api`, `web`, your own `admin`, …) belongs to that stack — at any depth, with that stack's middleware, prefix, name prefix and domain applied automatically.

Conventions are simpler than configs: no rename of existing files, no central index to maintain, and a custom stack works as soon as the folder exists.

> **Migration note.** If a folder under your scanned `paths[]` happens to be named after a stack key (`api/`, `web/`, …) and contains non-route PHP files (helpers, classes you forgot to namespace, …), those files will now be loaded as routes. Either narrow your `paths` config or rename the folder.

### Explicit discovery for advanced setups

[](#explicit-discovery-for-advanced-setups)

For explicit control, set `auto_discover_on_boot => false` and call from your `AppServiceProvider::boot()` (recommended location — facades are guaranteed to be ready there):

```
// app/Providers/AppServiceProvider.php
use Kirago\Routify\Facades\Routify;

public function boot(): void
{
    Routify::discover();              // every enabled stack
    Routify::discoverApi();           // only the api stack
    Routify::discoverApi('api/v1');   // override the prefix
    Routify::discoverWeb();
    Routify::discoverConsole();
    Routify::discoverChannels();
}
```

---

Configuration
-------------

[](#configuration)

`config/routify.php`:

```
return [

    // Absolute root directories scanned recursively. Missing paths
    // throw at scan time — no silent "found nothing" misconfigs.
    'paths' => [
        app_path('Modules'),
    ],

    // When true, every enabled stack is loaded as soon as the package
    // boots. When false, you drive discovery explicitly via the facade.
    'auto_discover_on_boot' => true,

    'stacks' => [
        'web' => [
            'enabled'    => true,
            'pattern'    => 'web*.php',
            'middleware' => ['web'],
            'prefix'     => null,
            'name'       => null,
            'domain'     => null,
        ],
        'api' => [
            'enabled'    => true,
            'pattern'    => 'api*.php',
            'middleware' => ['api'],
            'prefix'     => 'api',
            'name'       => 'api.',
            'domain'     => null,
        ],
        'console' => [
            'enabled'    => true,
            'pattern'    => 'console*.php',
            'middleware' => [],
            'prefix'     => null,
            'name'       => null,
            'domain'     => null,
        ],
        'channels' => [
            'enabled'    => true,
            'pattern'    => 'channels*.php',
            'middleware' => [],
            'prefix'     => null,
            'name'       => null,
            'domain'     => null,
        ],
    ],

    'cache' => [
        'enabled' => env('ROUTIFY_CACHE', false),
        'key'     => 'routify:files',
        'store'   => null, // null = the default cache store
    ],
];
```

### Custom stacks

[](#custom-stacks)

Declare anything you want — `routify` does not assume `web` and `api` are the only valid stacks:

```
'stacks' => [
    // …
    'admin' => [
        'enabled'    => true,
        'pattern'    => 'admin*.php',
        'middleware' => ['web', 'auth', 'admin'],
        'prefix'     => 'admin',
        'name'       => 'admin.',
        'domain'     => null,
    ],
],
```

Then load it explicitly:

```
Routify::for('admin')->load();
```

### Multiple paths

[](#multiple-paths)

```
'paths' => [
    app_path('Modules'),
    app_path('Features'),
    base_path('packages'),
],
```

Each path is scanned independently. If two paths overlap, the same file is registered exactly once.

---

Fluent builder
--------------

[](#fluent-builder)

For the cases the config can't express:

```
Routify::for('api')
    ->in(app_path('Modules'))           // override paths (one or many)
    ->in(base_path('packages/billing'))
    ->withPrefix('api/v2')              // override URL prefix
    ->withMiddleware(['api', 'throttle:60,1'])
    ->withName('api.v2.')               // route-name prefix
    ->withDomain('{tenant}.example.com')
    ->matching('api-v2*.php')           // override the glob pattern
    ->load();
```

Every method returns the builder, so order is irrelevant. `load()` is the terminal call.

---

Artisan commands
----------------

[](#artisan-commands)

```
php artisan routify:list                # tabular view of every discovered file
php artisan routify:list --stack=api    # restrict to one stack
php artisan routify:cache               # warm the discovery cache
php artisan routify:clear               # invalidate the discovery cache
php artisan routify:optimize            # clear + cache in one call
```

`routify:cache`, `routify:clear` and `routify:optimize` only do useful work when `routify.cache.enabled` is `true` (typically `ROUTIFY_CACHE=true` in production). The cache is on the filesystem scan — Laravel's own `route:cache` is orthogonal and complementary.

---

Production deployment
---------------------

[](#production-deployment)

In production, scanning the filesystem on every boot is wasteful. Enable the cache and warm it at deploy time:

```
# .env (production)
ROUTIFY_CACHE=true
```

Add the warm-up to the deployment script alongside the standard Laravel optimisations:

```
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan routify:optimize     # clears stale entries, re-warms the discovery cache
```

### CI/CD via Composer scripts

[](#cicd-via-composer-scripts)

To trigger Routify's cache refresh automatically on `composer install` / `composer update`, add this to **your application's** `composer.json`(not the package — Composer scripts live in the consuming app):

```
{
    "scripts": {
        "post-install-cmd": [
            "@php artisan routify:optimize --ansi"
        ],
        "post-update-cmd": [
            "@php artisan routify:optimize --ansi"
        ]
    }
}
```

The `routify:optimize` call is a no-op when the cache is disabled, so it is safe to keep enabled across all environments.

---

Security considerations
-----------------------

[](#security-considerations)

Routify `require`s the route files it discovers. The same trust model as Laravel's own `routes/` directory applies — with one important shift: the list of paths now comes from your config, so the surface for misconfig is larger. Keep these in mind:

- **Never put a writable directory in `routify.paths`.** Anything an attacker can write to (uploads dir, runtime cache, tmp) becomes a code execution vector at boot if Routify scans it.
- **Symlinks are followed by default** (Symfony Finder behaviour). On multi-tenant or shared-hosting setups where tenant-controlled directories live under your scan paths, audit the symlinks.
- **Cache poisoning = boot RCE.** If `routify.cache.enabled = true` and the cache backend is compromised, an attacker can inject arbitrary file paths into the cache. This is the standard Laravel cache trust model — protect your Redis/Memcached/database cache the same way you protect `bootstrap/cache/`.

In short: treat `routify.paths` like you treat the `routes/` directory. Don't stage anything writable by the runtime web user under it.

---

Testing
-------

[](#testing)

```
composer test            # runs the Pest suite via Orchestra Testbench
composer test:coverage   # with coverage (requires Xdebug or PCOV)
composer format:test     # Laravel Pint --test
composer analyse         # PHPStan
```

---

Architecture
------------

[](#architecture)

The why-and-how of every architectural choice is documented as Architecture Decision Records under [`docs/adr/`](docs/adr/) (in French). Start with [ADR-0001 — Layered architecture](docs/adr/0001-architecture-en-couches.md)for the big picture.

---

Roadmap
-------

[](#roadmap)

Post-1.0 ideas, not committed:

- per-stack `Route::scopeBindings()`
- OpenAPI doc generation from discovered routes
- `pestphp/pest-plugin-laravel` assertions (`expect()->toHaveDiscoveredRoute('api.users.index')`)
- multi-tenant dynamic paths
- bridging the discovery cache into Laravel's native `route:cache` mechanism

---

Contributing
------------

[](#contributing)

Issues and pull requests on [GitHub](https://github.com/kirago-tech/laravel-routify) are welcome. Please run `composer test` and `composer format:test` before submitting.

---

License
-------

[](#license)

MIT — see [LICENSE.md](LICENSE.md). Copyright © 2026 [Simo Joel](mailto:joel.simo@kirago.tech).

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance97

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 74.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~15 days

Total

3

Last Release

14d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cfa236348aa735b827bfcc903ccfb3091606d6db9de6a02892b1ead56ab0d593?d=identicon)[jsimo-kirago](/maintainers/jsimo-kirago)

---

Top Contributors

[![jsimo-kirago](https://avatars.githubusercontent.com/u/273799457?v=4)](https://github.com/jsimo-kirago "jsimo-kirago (23 commits)")[![jsimo237](https://avatars.githubusercontent.com/u/46076236?v=4)](https://github.com/jsimo237 "jsimo237 (8 commits)")

---

Tags

laravelroutesmodulesroute-discoveryauto-discoverykiragoroutify

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/kirago-laravel-routify/health.svg)

```
[![Health](https://phpackages.com/badges/kirago-laravel-routify/health.svg)](https://phpackages.com/packages/kirago-laravel-routify)
```

###  Alternatives

[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M110](/packages/laravel-mcp)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[laravel/sail

Docker files for running a basic Laravel application.

1.9k199.2M1.2k](/packages/laravel-sail)[laravel/boost

Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.

3.5k17.6M507](/packages/laravel-boost)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k28.4M134](/packages/laravel-cashier)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
