Routing
Routes define how your application responds to HTTP requests. Glueful provides a fast, intuitive routing system with O(1) static lookups and flexible middleware support.
Basic Routes
Define routes in routes/api.php
:
use Glueful\Http\Response;
// Simple closure
$router->get('/hello', fn() => new Response(['message' => 'Hello World']));
// Controller method
$router->get('/users', [UserController::class, 'index']);
$router->post('/users', [UserController::class, 'store']);
HTTP Methods
$router->get('/users', ...); // GET
$router->post('/users', ...); // POST
$router->put('/users/{id}', ...); // PUT
$router->delete('/users/{id}', ...); // DELETE
$router->head('/status', ...); // HEAD
Route Parameters
Basic Parameters
$router->get('/users/{id}', function (int $id) {
return Response::success(['user_id' => $id]);
});
Multiple Parameters
$router->get('/posts/{year}/{month}/{slug}', function ($year, $month, $slug) {
// Access parameters by position or name
});
Parameter Constraints
Validate parameters with regex patterns:
// Numeric ID only
$router->get('/users/{id}', [UserController::class, 'show'])
->where('id', '\d+');
// Multiple constraints
$router->get('/posts/{year}/{month}', [PostController::class, 'index'])
->where([
'year' => '\d{4}',
'month' => '\d{2}'
]);
// Slug pattern
$router->get('/blog/{slug}', [BlogController::class, 'show'])
->where('slug', '[a-z0-9-]+');
Route Groups
Share prefixes and middleware across multiple routes:
$router->group(['prefix' => '/api/v1'], function ($router) {
$router->get('/users', [UserController::class, 'index']);
$router->get('/posts', [PostController::class, 'index']);
});
// Creates: /api/v1/users, /api/v1/posts
Group with Middleware
$router->group(['prefix' => '/admin', 'middleware' => ['auth', 'admin']], function ($router) {
$router->get('/dashboard', [AdminController::class, 'dashboard']);
$router->get('/users', [AdminController::class, 'users']);
});
Nested Groups
$router->group(['prefix' => '/api'], function ($router) {
$router->group(['prefix' => '/v1', 'middleware' => ['auth']], function ($router) {
$router->get('/profile', [ProfileController::class, 'show']);
});
});
// Creates: /api/v1/profile with auth middleware
Middleware
Route-Level Middleware
$router->get('/dashboard', [DashboardController::class, 'index'])
->middleware('auth');
// Multiple middleware
$router->get('/admin', [AdminController::class, 'index'])
->middleware(['auth', 'admin']);
// Parameterized middleware
$router->post('/api/upload', [UploadController::class, 'store'])
->middleware('rate_limit:10,60'); // 10 requests per 60 seconds
Available Middleware
Middleware | Purpose | Parameters |
---|---|---|
auth | JWT authentication | None |
cors | CORS headers | None |
csrf | CSRF protection | None |
rate_limit | Rate limiting | maxAttempts,windowSeconds |
Dependency Injection
Auto-inject services into route handlers:
use Psr\Log\LoggerInterface;
$router->get('/users/{id}', function (int $id, UserService $service, LoggerInterface $logger) {
$logger->info('Fetching user', ['id' => $id]);
return Response::success($service->find($id));
});
Named Routes
Name routes for easy URL generation:
$router->get('/users/{id}/profile', [ProfileController::class, 'show'])
->name('user.profile');
// Generate URL
$url = route('user.profile', ['id' => 123]); // /users/123/profile
Route Attributes (PHP 8+)
Define routes directly in controllers using attributes:
use Glueful\Http\Routing\Attributes\Route;
class UserController
{
#[Route('GET', '/users')]
public function index() {
// ...
}
#[Route('GET', '/users/{id}', where: ['id' => '\d+'])]
public function show(int $id) {
// ...
}
#[Route('POST', '/users', middleware: ['auth'])]
public function store() {
// ...
}
}
Controller Resolution
When you use the array callable form [Controller::class, 'method']
, the router resolves the controller via the DI container and then calls the method. This enables constructor injection and consistent lifecycle handling.
- Requirement: Your controller class must be container‑resolvable (registered or autowire‑able).
- Recommended: Register app controllers in an app service provider so they can be auto‑wired.
Example provider (App\Providers\AppServiceProvider):
<?php
namespace App\Providers;
final class AppServiceProvider
{
// Provider discovery loads this static method to register services
public static function services(): array
{
return [
// Autowire controllers so [Controller::class, 'method'] works
\App\Controllers\WelcomeController::class => [
'autowire' => true,
'shared' => true,
],
];
}
}
Enable the provider in config/serviceproviders.php
:
return [
'enabled' => [
App\Providers\AppServiceProvider::class,
],
];
If you don’t need DI for a route, you can also use a closure and manually instantiate the controller:
use Symfony\Component\HttpFoundation\Request;
$router->get('/index', function (Request $request) {
return (new WelcomeController())->index($request);
});
Performance Tips
Route Caching
Cache routes in production for faster lookup:
# Cache routes
php glueful route:cache
# Clear cache
php glueful route:clear
Avoid Dynamic Routes
// ❌ Avoid - creates many routes
for ($i = 1; $i <= 100; $i++) {
$router->get("/page{$i}", ...);
}
// ✅ Better - use parameters
$router->get('/page/{number}', ...)
->where('number', '\d+');
Common Patterns
RESTful Resources
// Full CRUD for a resource
$router->get('/posts', [PostController::class, 'index']); // List
$router->post('/posts', [PostController::class, 'store']); // Create
$router->get('/posts/{id}', [PostController::class, 'show']); // Read
$router->put('/posts/{id}', [PostController::class, 'update']); // Update
$router->delete('/posts/{id}', [PostController::class, 'destroy']); // Delete
API Versioning
$router->group(['prefix' => '/api/v1'], function ($router) {
require __DIR__ . '/api/v1.php';
});
$router->group(['prefix' => '/api/v2'], function ($router) {
require __DIR__ . '/api/v2.php';
});
Protected Routes
// Public routes
$router->post('/login', [AuthController::class, 'login']);
$router->post('/register', [AuthController::class, 'register']);
// Protected routes
$router->group(['middleware' => ['auth']], function ($router) {
$router->get('/profile', [ProfileController::class, 'show']);
$router->put('/profile', [ProfileController::class, 'update']);
$router->delete('/account', [AccountController::class, 'delete']);
});
Troubleshooting
Route not found (404)?
- Check route definition spelling and parameters
- Verify middleware isn't blocking the route
- Clear route cache:
php glueful route:clear
Parameter not passed to handler?
- Ensure parameter names match route definition
- Check type hints match expected data
Middleware not executing?
- Verify middleware is registered in service provider
- Check middleware order in groups
Next Steps
- Controllers - Handle route logic
- Middleware - Custom request processing
- Requests & Responses - Work with HTTP data