Navigation

Laravel

How to handle optional parameters in API routes

Handle optional route parameters in Laravel APIs with flexible URL patterns. Perfect for search endpoints, filtering, and variable route structures.

Table Of Contents

Problem

You need API routes that work with or without certain parameters, like /api/users and /api/users/{category} using the same controller method.

Solution

Use the ? operator to make route parameters optional:

// routes/api.php
Route::get('/users/{category?}', [UserController::class, 'index']);

// Controller
public function index(Request $request, $category = null)
{
    $query = User::query();
    
    if ($category) {
        $query->where('category', $category);
    }
    
    return $query->get();
}

Multiple optional parameters:

// Route with multiple optional parameters
Route::get('/posts/{category?}/{tag?}', [PostController::class, 'index']);

public function index(Request $request, $category = null, $tag = null)
{
    $query = Post::query();
    
    if ($category) {
        $query->where('category', $category);
    }
    
    if ($tag) {
        $query->whereHas('tags', fn($q) => $q->where('name', $tag));
    }
    
    return $query->paginate(15);
}

Alternative approach using query parameters:

// Single route handling optional filtering
Route::get('/products', [ProductController::class, 'index']);

public function index(Request $request)
{
    $query = Product::query();
    
    // Handle optional filters via query params
    if ($request->has('category')) {
        $query->where('category_id', $request->category);
    }
    
    if ($request->has('price_min')) {
        $query->where('price', '>=', $request->price_min);
    }
    
    if ($request->has('search')) {
        $query->where('name', 'like', '%' . $request->search . '%');
    }
    
    return $query->get();
}

Why It Works

Adding ? after a route parameter makes it optional. Laravel will match both /users and /users/electronics to the same route. The controller receives null for missing parameters, allowing you to handle different scenarios in the same method.

Advanced pattern with constraints:

// Optional parameter with validation
Route::get('/users/{id?}', [UserController::class, 'show'])
     ->where('id', '[0-9]+');

// Multiple routes for better control
Route::get('/api/search', [SearchController::class, 'all']);
Route::get('/api/search/{type}', [SearchController::class, 'byType'])
     ->where('type', 'users|products|posts');

public function byType(Request $request, string $type)
{
    return match($type) {
        'users' => User::search($request->q)->get(),
        'products' => Product::search($request->q)->get(),
        'posts' => Post::search($request->q)->get(),
    };
}

Related: Laravel Collections: Beyond Basic Array Operations | Laravel Events and Listeners: Building Decoupled Applications | Laravel API Development: Best Practices and Security | Laravel Route Model Binding: Beyond the Basics | REST API Design Best Practices Tutorial 2025

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Laravel