Documentation Index Fetch the complete documentation index at: https://mintlify.com/internachi/modular/llms.txt
Use this file to discover all available pages before exploring further.
Laravel Modular automatically loads route files from your modules, making it easy to keep your routes organized alongside your module code.
Route Files
All PHP files in a module’s routes/ directory are automatically loaded. Routes are loaded alphabetically by filename.
Default Route File
When you create a module, a default route file is generated:
app-modules/blog/routes/blog-routes.php
<? php
use Illuminate\Support\Facades\ Route ;
Route :: get ( '/posts' , function () {
return 'Blog posts' ;
});
Route files are loaded automatically - you don’t need to register them manually.
Organizing Routes
You can organize routes into multiple files within the routes/ directory:
app-modules/blog/
└── routes/
├── api.php
├── web.php
└── admin.php
Web Routes
app-modules/blog/routes/web.php
<? php
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Controllers\ PostController ;
Route :: middleware ( 'web' ) -> group ( function () {
Route :: get ( '/posts' , [ PostController :: class , 'index' ])
-> name ( 'blog.posts.index' );
Route :: get ( '/posts/{post}' , [ PostController :: class , 'show' ])
-> name ( 'blog.posts.show' );
});
API Routes
app-modules/blog/routes/api.php
<? php
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Controllers\Api\ PostController ;
Route :: middleware ( 'api' ) -> prefix ( 'api' ) -> group ( function () {
Route :: apiResource ( 'posts' , PostController :: class );
});
Admin Routes
app-modules/blog/routes/admin.php
<? php
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Controllers\Admin\ PostController ;
Route :: middleware ([ 'web' , 'auth' , 'admin' ]) -> prefix ( 'admin' ) -> group ( function () {
Route :: resource ( 'posts' , PostController :: class );
});
Route Naming Conventions
Prefix route names with your module name to avoid conflicts: Route :: get ( '/posts' , [ PostController :: class , 'index' ])
-> name ( 'blog.posts.index' );
This prevents naming conflicts when multiple modules have similar routes:
// Blog module
Route :: name ( 'blog.' ) -> group ( function () {
Route :: get ( '/posts' , ... ) -> name ( 'posts.index' ); // blog.posts.index
Route :: get ( '/posts/{post}' , ... ) -> name ( 'posts.show' ); // blog.posts.show
});
Route Groups
Group routes by module features using route groups:
app-modules/blog/routes/web.php
<? php
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Controllers\ PostController ;
use Modules\Blog\Controllers\ CommentController ;
// Public routes
Route :: prefix ( 'blog' ) -> name ( 'blog.' ) -> group ( function () {
Route :: get ( '/' , [ PostController :: class , 'index' ]) -> name ( 'index' );
Route :: get ( '/posts/{post}' , [ PostController :: class , 'show' ]) -> name ( 'posts.show' );
});
// Authenticated routes
Route :: middleware ( 'auth' ) -> prefix ( 'blog' ) -> name ( 'blog.' ) -> group ( function () {
Route :: post ( '/posts/{post}/comments' , [ CommentController :: class , 'store' ])
-> name ( 'comments.store' );
});
Route Model Binding
Use route model binding with your module models:
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Models\ Post ;
use Modules\Blog\Controllers\ PostController ;
Route :: get ( '/posts/{post}' , [ PostController :: class , 'show' ]);
In your controller:
app-modules/blog/src/Controllers/PostController.php
<? php
namespace Modules\Blog\Controllers ;
use App\Http\Controllers\ Controller ;
use Modules\Blog\Models\ Post ;
class PostController extends Controller
{
public function show ( Post $post )
{
return view ( 'blog::posts.show' , compact ( 'post' ));
}
}
Custom Route Key
Customize the route key in your model:
app-modules/blog/src/Models/Post.php
<? php
namespace Modules\Blog\Models ;
use Illuminate\Database\Eloquent\ Model ;
class Post extends Model
{
public function getRouteKeyName ()
{
return 'slug' ;
}
}
Now routes will bind using the slug:
// Matches /posts/my-first-post instead of /posts/1
Route :: get ( '/posts/{post}' , [ PostController :: class , 'show' ]);
Resource Routes
Use resource routing for CRUD operations:
use Modules\Blog\Controllers\ PostController ;
Route :: resource ( 'posts' , PostController :: class );
This creates all standard RESTful routes:
GET /posts index
GET /posts/create create
POST /posts store
GET /posts/{post} show
GET /posts/{post}/edit edit
PUT /posts/{post} update
DELETE /posts/{post} destroy
API Resources
For APIs, use apiResource to exclude create and edit routes:
Route :: apiResource ( 'posts' , PostController :: class );
Middleware
Apply middleware to module routes:
Route-Level Middleware
Route :: middleware ([ 'auth' , 'verified' ]) -> group ( function () {
Route :: get ( '/dashboard' , [ DashboardController :: class , 'index' ]);
});
Module Middleware
Create middleware specific to your module:
php artisan make:middleware CheckBlogAccess --module=blog
Register it in your module’s service provider:
app-modules/blog/src/Providers/BlogServiceProvider.php
<? php
namespace Modules\Blog\Providers ;
use Illuminate\Support\ ServiceProvider ;
use Illuminate\Routing\ Router ;
use Modules\Blog\Middleware\ CheckBlogAccess ;
class BlogServiceProvider extends ServiceProvider
{
public function boot ( Router $router )
{
$router -> aliasMiddleware ( 'blog.access' , CheckBlogAccess :: class );
}
}
Use it in routes:
Route :: middleware ( 'blog.access' ) -> group ( function () {
// Protected routes
});
Route Caching
Module routes work with Laravel’s route caching, but make sure to clear and recache after adding new routes.
Cache routes for production:
Clear route cache during development:
Loading Order
Route files are loaded alphabetically by filename across all modules:
1. app-modules/blog/routes/api.php
2. app-modules/blog/routes/web.php
3. app-modules/shop/routes/api.php
4. app-modules/shop/routes/web.php
Prefix filenames with numbers to control loading order if needed:
01-api.php
02-web.php
03-admin.php
Example: Complete Routing Setup
Here’s a complete example of a well-organized module routing setup:
app-modules/blog/routes/web.php
<? php
use Illuminate\Support\Facades\ Route ;
use Modules\Blog\Controllers\ PostController ;
use Modules\Blog\Controllers\ CommentController ;
use Modules\Blog\Controllers\ CategoryController ;
// Public blog routes
Route :: prefix ( 'blog' ) -> name ( 'blog.' ) -> group ( function () {
// Posts
Route :: get ( '/' , [ PostController :: class , 'index' ]) -> name ( 'index' );
Route :: get ( '/posts/{post:slug}' , [ PostController :: class , 'show' ]) -> name ( 'posts.show' );
// Categories
Route :: get ( '/categories/{category:slug}' , [ CategoryController :: class , 'show' ])
-> name ( 'categories.show' );
});
// Authenticated user routes
Route :: middleware ( 'auth' ) -> prefix ( 'blog' ) -> name ( 'blog.' ) -> group ( function () {
// Comments
Route :: post ( '/posts/{post}/comments' , [ CommentController :: class , 'store' ])
-> name ( 'comments.store' );
Route :: delete ( '/comments/{comment}' , [ CommentController :: class , 'destroy' ])
-> name ( 'comments.destroy' );
});
// Admin routes
Route :: middleware ([ 'auth' , 'admin' ])
-> prefix ( 'admin/blog' )
-> name ( 'admin.blog.' )
-> group ( function () {
Route :: resource ( 'posts' , PostController :: class );
Route :: resource ( 'categories' , CategoryController :: class );
});
Testing Routes
Test your module routes:
app-modules/blog/tests/PostRoutesTest.php
<? php
namespace Modules\Blog\Tests ;
use Tests\ TestCase ;
use Modules\Blog\Models\ Post ;
class PostRoutesTest extends TestCase
{
public function test_can_view_posts_index ()
{
$response = $this -> get ( route ( 'blog.index' ));
$response -> assertStatus ( 200 );
}
public function test_can_view_single_post ()
{
$post = Post :: factory () -> create ();
$response = $this -> get ( route ( 'blog.posts.show' , $post ));
$response -> assertStatus ( 200 );
}
}
Next Steps
Module Views Learn how to create and use views in modules
Module Components Create controllers and other components