Laravel REST API with query parameters
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Query Parameters in Laravel REST APIs with Eloquent Joins
As developers building robust APIs with Laravel, we often encounter a common challenge: efficiently translating complex query parameters from an API request into optimized database queries, especially when dealing with resources that involve multiple table joins. The methods like Response::eloquent($query) are powerful, but understanding how to construct that initial $query is where the true performance and elegance of your Laravel application shine.
This post will dive deep into how to correctly handle query parameters for complex Eloquent resources, addressing the two core questions developers frequently face when designing RESTful endpoints.
The Challenge: Joins and Query Parameters
Consider a scenario where you are retrieving a list of Products, but you need to filter by the category (which requires joining the categories table) and sort by a related field, like the creation date of the associated Category.
If you simply fetch all the data and then try to filter it in application memory (PHP), you introduce significant performance bottlenecks. The solution lies in pushing the filtering and sorting logic down to the database layer using Eloquent’s query builder.
Best Practice: Filtering at the Database Level
The most efficient way to handle query parameters is to map them directly onto the Eloquent Query Builder methods. This ensures that only the necessary data is retrieved from the database, minimizing network latency and database load.
When handling a request like ?category=electronics&order_by=timestamp, you should translate these into corresponding Eloquent calls:
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
// 1. Start the base query on the main model
$query = Product::query();
// 2. Handle filtering (e.g., category=electronics)
if ($request->has('category')) {
$query->whereHas('category', function ($query) use ($request) {
$query->where('name', $request->input('category'));
});
}
// 3. Handle sorting (e.g., order_by=timestamp)
if ($request->has('order_by')) {
$sortBy = $request->input('order_by');
$direction = $request->input('direction', 'desc'); // Default direction
// Dynamically determine the sort field based on the parameter
if ($sortBy === 'timestamp') {
$query->orderBy('categories.created_at', $direction);
} else {
// Handle other sorting logic if necessary
$query->orderBy($sortBy, $direction);
}
}
// 4. Execute the query and return the results
$products = $query->with('category') // Eager load necessary relationships
->get();
return response()->json($products);
}
}
Explanation of the Approach:
Product::query(): We start the query builder chain on our primary model.whereHas()for Joins: When filtering based on a related model (likecategory), we usewhereHas(). This is crucial because it correctly handles the necessaryJOINoperation behind the scenes, ensuring that only products linked to matching categories are returned.- Dynamic Sorting: We check for parameters and dynamically apply
orderBy()clauses. By referencing the relationship table (e.g.,categories.created_at), we ensure the sorting logic operates correctly across the joined data set.
Selecting Specific Fields: Balancing Data Retrieval
Your second question—whether to fetch everything and filter in PHP, or select only necessary fields—is equally important for API performance. The answer is always the latter.
Never fetch more data than you need. If your API endpoint only needs product names, IDs, and category names, do not load large text fields or unnecessary relational data into memory if you don't use them.
This is where Eloquent’s select() method becomes invaluable:
// ... continuing from the previous example
$products = $query->with('category')
->select([
'products.*', // Select all columns from the main Product table
'categories.name as category_name', // Select specific fields from the joined relation
'categories.slug as category_slug'
])
->get();
return response()->json($products);
By using select(), you instruct the database to only return the columns required for the API response, significantly reducing the payload size and improving overall query speed. This principle of efficient data retrieval is a cornerstone of building high-performance Laravel applications, aligning perfectly with the principles taught by the Laravel team.
Conclusion
Handling complex query parameters in Laravel REST APIs requires shifting focus from application logic to database optimization. By leveraging Eloquent’s fluent query builder—using whereHas for joins and select for field pruning—you ensure that your API endpoints are not only functional but also performant, scalable, and maintainable. Always aim to let the database do the heavy lifting!