Table of Contents
π 2 minutes read
When mixing custom routes with apiResource, order matters. Specific routes must come before parameterized ones.
The Problem
// β WRONG: Custom route comes AFTER resource
Route::apiResource('notifications', NotificationController::class);
Route::get('/notifications/stats', [NotificationController::class, 'stats']);
Result:
GET /api/notifications/stats
# Laravel matches this to show($id='stats') β
# Returns 404: Notification not found
The Solution
// β
CORRECT: Custom routes BEFORE resource
Route::get('/notifications/stats', [NotificationController::class, 'stats'])
->name('notifications.stats');
Route::post('/notifications/actions/mark-all-read', [NotificationController::class, 'markAllAsRead'])
->name('notifications.mark-all-read');
Route::apiResource('notifications', NotificationController::class)
->only(['index', 'show', 'update', 'destroy']);
Route Matching Order
Laravel matches routes top to bottom:
GET /notifications/statsβ matches first route βPOST /notifications/actions/mark-all-readβ matches second route βGET /notifications/abc123β matchesapiResourceshow route β
Naming Convention for Custom Routes
Use descriptive paths for clarity:
// Collection-level actions
Route::get('/users/export', ...); // GET /api/users/export
Route::post('/users/import', ...); // POST /api/users/import
// Named action routes
Route::post('/orders/actions/bulk-cancel', ...);
Route::get('/products/stats', ...);
// Avoid generic prefixes that might conflict
Route::get('/notifications/meta', ...); // β Good: 'meta' won't be a UUID
Route::get('/notifications/all', ...); // β οΈ Could conflict if 'all' is valid ID
Debug Routes
Use php artisan route:list to verify order:
php artisan route:list --path=notifications
# Output shows registration order:
GET api/notifications/stats notifications.stats
POST api/notifications/actions/... notifications.mark-all-read
GET api/notifications notifications.index
GET api/notifications/{notification} notifications.show
Remember: Specific beats generic. Always define custom routes before resource routes.
Leave a Reply