Composing Clean API Queries with jQuery DataTables and Atomic Query Construction
Muhammad Raheel
March 23rd, 2026 · 5 min read
When I work with jQuery DataTables, I only send filters that actually have values. Nothing more, nothing less. For example:
"data": function (d) {
if ($('#category_id').val() > 0) {
d['category_id'] = $('#category_id').val();
}
if ($('#brand_id').val() > 0) {
d['brand_id'] = $('#brand_id').val();
}
if ($('#keyword').val() != "") {
d['keyword'] = $('#keyword').val();
}
}
Each filter is included only if it exists. Empty or irrelevant values are ignored. This ensures the backend only receives meaningful data.
Backend with AQC: Composition in Action
On the backend, I use the Atomic Query Construction (AQC) pattern. AQC lets us compose small, independent filter blocks, applying only the filters that exist in the request. For example:
$query = Product::query();
if ($this->category_id) {
$query->where('category_id', $this->category_id);
}
if ($this->brand_id) {
$query->where('brand_id', $this->brand_id);
}
if ($this->keyword) {
$query->where('name', 'like', "%{$this->keyword}%");
}
return $query->get();
Here, each block is independent. Since we have three optional filters, there are eight possible combinations of these filters:
- No filter means fetch all
- category_id only
- brand_id only
- keyword only
- category_id + brand_id
- category_id + keyword
- brand_id + keyword
- category_id + brand_id + keyword
AQC handles all these combinations automatically. There’s no need for:
- Repository pattern just to separate queries
- Query object pattern for every possible filter combination
Everything is handled by composing small, reusable filter blocks. Adding a new filter is as simple as adding another block. if
if ($this->status) {
$query->where('status', $this->status);
}
All combinations now include the new filter naturally, without touching existing code.
Frontend and Backend Alignment
The frontend decides which filters are active. The backend applies only those filters using AQC composition.
| Layer | Responsibility |
|---|---|
| Frontend | Send only active filters |
| Backend | Apply filters conditionally and compose query |
Why Composition Matters
- Each filter is independent and reusable
- All possible combinations of filters are handled automatically
- No duplication of queries or classes
- No need for repository or query object patterns for conditional filtering
- Adding a new filter doesn’t break existing code
- Frontend and backend stay aligned naturally
By combining conditional frontend filters with AQC composition, filtering becomes clean, scalable, and flexible — no matter how many parameters exist.
Final Thoughts
When filters are optional, the real problem is not writing queries. It’s handling combinations.
With three filters, there are already eight combinations. Add more, and it grows fast. Trying to handle this manually with repositories, query objects, or separate methods only increases complexity.
AQC solves this by using composition. Each filter is a small, independent piece, and the final query is built by combining only what exists.
On the frontend, jQuery DataTables already gives you a simple way to send only active filters. On the backend, AQC applies only those filters. Both sides follow the same rule, without extra coordination.
This removes unnecessary patterns, reduces duplication, and keeps the code easy to extend.
In the end, it’s not about DataTables or AQC separately. It’s about keeping both layers consistent and letting composition handle the complexity instead of writing it yourself.