Skip to main content

Querying and Filtering Data

Learn how to retrieve, filter, sort, and paginate your data using powerful query capabilities.

Basic Querying

Get All Records

Retrieve all records from a table:

GET /api/apps/{app-slug}/datatables/{table-name}/data/

Example:

GET /api/apps/blog-app/datatables/users/data/

Response:

{
"data": [
{
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": 2,
"username": "jane_smith",
"email": "jane@example.com",
"created_at": "2024-01-16T14:20:00Z"
}
],
"total": 2
}

Get Single Record

Retrieve a specific record by ID:

GET /api/apps/{app-slug}/datatables/{table-name}/data/{id}/

Example:

GET /api/apps/blog-app/datatables/users/data/1/

Response:

{
"data": {
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"bio": "Software developer",
"created_at": "2024-01-15T10:30:00Z"
}
}

Filtering

Filter records using query parameters with powerful operators.

Exact Match

GET /api/apps/blog-app/datatables/posts/data/?status=published

Comparison Operators

OperatorDescriptionExample
_gtGreater than?views_gt=100
_gteGreater than or equal?score_gte=80
_ltLess than?age_lt=30
_lteLess than or equal?price_lte=50
_neNot equal?status_ne=draft

Examples:

# Posts with more than 100 views
GET /api/apps/blog-app/datatables/posts/data/?views_gt=100

# Products under $50
GET /api/apps/shop-app/datatables/products/data/?price_lte=50

# Users not from USA
GET /api/apps/crm-app/datatables/users/data/?country_ne=USA

IN Operators

OperatorDescriptionCase Sensitivity
_inValue in listCase-sensitive
_ninValue not in listCase-sensitive
_inaValue in listCase-insensitive
_ninaValue not in listCase-insensitive

Examples:

# Posts with specific statuses
GET /api/apps/blog-app/datatables/posts/data/?status_in=published,featured

# Exclude certain categories
GET /api/apps/blog-app/datatables/posts/data/?category_nin=spam,archived

# Case-insensitive search
GET /api/apps/blog-app/datatables/posts/data/?tag_ina=javascript,PYTHON,Ruby

String Matching

Contains

OperatorDescriptionCase Sensitivity
_containsContains substringCase-insensitive
_ncontainsDoes not containCase-insensitive
_containssContains substringCase-sensitive
_ncontainssDoes not containCase-sensitive

Examples:

# Titles containing "django"
GET /api/apps/blog-app/datatables/posts/data/?title_contains=django

# Emails not containing "spam"
GET /api/apps/crm-app/datatables/users/data/?email_ncontains=spam

# Case-sensitive search
GET /api/apps/docs-app/datatables/articles/data/?content_containss=API

Starts With

OperatorDescriptionCase Sensitivity
_startswithStarts withCase-insensitive
_nstartswithDoes not start withCase-insensitive
_startswithsStarts withCase-sensitive
_nstartswithsDoes not start withCase-sensitive

Examples:

# Usernames starting with "admin"
GET /api/apps/blog-app/datatables/users/data/?username_startswith=admin

# Products not starting with "test"
GET /api/apps/shop-app/datatables/products/data/?sku_nstartswith=test

Ends With

OperatorDescriptionCase Sensitivity
_endswithEnds withCase-insensitive
_nendswithDoes not end withCase-insensitive
_endswithsEnds withCase-sensitive
_nendswithsDoes not end withCase-sensitive

Examples:

# Emails ending with specific domain
GET /api/apps/crm-app/datatables/users/data/?email_endswith=company.com

# Files not ending with .tmp
GET /api/apps/docs-app/datatables/files/data/?filename_nendswith=.tmp

Range Queries

OperatorDescriptionFormat
_betweenValue between range?field_between=min,max
_nbetweenValue not in range?field_nbetween=min,max

Examples:

# Posts created in January 2024
GET /api/apps/blog-app/datatables/posts/data/?created_at_between=2024-01-01,2024-01-31

# Products outside price range
GET /api/apps/shop-app/datatables/products/data/?price_nbetween=10,20

Null Checks

OperatorDescriptionValue
_nullIs null/not nulltrue or false
_nnullIs not null/nulltrue or false

Examples:

# Users without bio
GET /api/apps/blog-app/datatables/users/data/?bio_null=true

# Posts with published date
GET /api/apps/blog-app/datatables/posts/data/?published_at_nnull=true

Sorting

Sort results by one or more fields.

Single Field

# Sort by username (ascending)
GET /api/apps/blog-app/datatables/users/data/?_sort=username

# Sort by created date (descending)
GET /api/apps/blog-app/datatables/posts/data/?_sort=created_at&_order=desc

Multiple Fields

# Sort by status, then by created_at descending
GET /api/apps/blog-app/datatables/posts/data/?_sort=status,created_at&_order=asc,desc

Parameters:

  • _sort: Comma-separated list of field names
  • _order: Comma-separated list of asc or desc (default: asc)

Pagination

Control the number of results returned.

Basic Pagination

# Get first 10 records
GET /api/apps/blog-app/datatables/posts/data/?_start=0&_end=10

# Get records 10-20
GET /api/apps/blog-app/datatables/posts/data/?_start=10&_end=20

# Get 50 records starting from 100
GET /api/apps/blog-app/datatables/posts/data/?_start=100&_end=150

Parameters:

  • _start: Starting index (0-based)
  • _end: Ending index (exclusive)

Response Format: The response includes total count and meta with pagination information:

{
"data": [...],
"total": 250,
"meta": {
"offset": 0,
"limit": 10,
"count": 10,
"has_more": true
}
}

Calculating Pages

const pageSize = 20;
const page = 1; // First page

const start = (page - 1) * pageSize;
const end = page * pageSize;

const url = `/api/apps/blog-app/datatables/posts/data/?_start=${start}&_end=${end}`;

Combining Filters

You can combine multiple filters, sorting, and pagination:

GET /api/apps/blog-app/datatables/posts/data/?\
status=published&\
views_gt=100&\
category_in=tech,programming&\
title_contains=python&\
_sort=views,created_at&\
_order=desc,desc&\
_start=0&\
_end=20

This query:

  1. ✅ Filters published posts
  2. ✅ With more than 100 views
  3. ✅ In tech or programming categories
  4. ✅ Title containing "python"
  5. ✅ Sorted by views (high to low), then by date (newest first)
  6. ✅ Returns first 20 results

Response Format

List Response

All list endpoints return a standardized response with data, total, and meta:

{
"data": [
{
"id": 1,
"title": "Getting Started with Python",
"status": "published",
"views": 1523,
"category": "programming",
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": 5,
"title": "Advanced Python Techniques",
"status": "published",
"views": 856,
"category": "tech",
"created_at": "2024-01-20T14:20:00Z"
}
],
"total": 42,
"meta": {
"offset": 0,
"limit": 20,
"count": 2,
"has_more": true
}
}

Response Fields

  • data: Array of records matching the query
  • total: Total count of records matching filters (not just current page)
  • meta: Pagination metadata
    • offset: Starting position of current page
    • limit: Maximum records per page
    • count: Number of records in current response
    • has_more: Whether there are more records available

Use total and meta to implement pagination UI.

Examples by Use Case

Search Functionality

# Search users by name or email
GET /api/apps/crm-app/datatables/users/data/?\
name_contains=john&\
email_contains=@company.com

Recent Items

# Last 10 posts
GET /api/apps/blog-app/datatables/posts/data/?\
_sort=created_at&\
_order=desc&\
_start=0&\
_end=10
# Most viewed posts this month
GET /api/apps/blog-app/datatables/posts/data/?\
created_at_between=2024-01-01,2024-01-31&\
_sort=views&\
_order=desc&\
_start=0&\
_end=10

Active Users

# Users who logged in recently
GET /api/apps/crm-app/datatables/users/data/?\
last_login_nnull=true&\
_sort=last_login&\
_order=desc

Status Filtering

# Pending orders
GET /api/apps/shop-app/datatables/orders/data/?\
status=pending&\
_sort=created_at&\
_order=asc

Client Libraries

JavaScript/TypeScript

const fetchUsers = async (page = 1, pageSize = 20) => {
const start = (page - 1) * pageSize;
const end = page * pageSize;

const params = new URLSearchParams({
status: 'active',
_sort: 'created_at',
_order: 'desc',
_start: start.toString(),
_end: end.toString(),
});

const response = await fetch(
`/api/apps/blog-app/datatables/users/data/?${params}`,
{
headers: {
'Authorization': `Bearer ${token}`,
},
}
);

const result = await response.json();

return {
data: result.data,
total: result.total,
totalPages: Math.ceil(result.total / pageSize),
meta: result.meta,
hasMore: result.meta.has_more,
};
};

Python

import requests

def fetch_posts(
app_slug='blog-app',
status='published',
page=1,
page_size=20,
sort_by='created_at',
order='desc'
):
start = (page - 1) * page_size
end = page * page_size

params = {
'status': status,
'_sort': sort_by,
'_order': order,
'_start': start,
'_end': end,
}

response = requests.get(
f'https://api.yourapp.com/api/apps/{app_slug}/datatables/posts/data/',
params=params,
headers={'Authorization': f'Bearer {token}'}
)

result = response.json()

return {
'data': result['data'],
'total': result['total'],
'total_pages': -(-result['total'] // page_size),
'meta': result.get('meta', {}),
'has_more': result.get('meta', {}).get('has_more', False),
}

Performance Tips

1. Use Specific Fields

Only select the fields you need (future feature):

# Coming soon: field selection
GET /api/apps/blog-app/datatables/posts/data/?fields=id,title,created_at

2. Limit Page Size

Don't request too many records at once:

# ✅ Good: Reasonable page size
?_start=0&_end=50

# ❌ Bad: Too many records
?_start=0&_end=10000

3. Use Indexes

Ensure your filtered and sorted fields have database indexes. Contact your administrator if queries are slow.

4. Cache Results

Cache frequently accessed data on the client side:

const cache = new Map();

async function fetchWithCache(url) {
if (cache.has(url)) {
return cache.get(url);
}

const response = await fetch(url);
const data = await response.json();

cache.set(url, data);
setTimeout(() => cache.delete(url), 60000); // Cache for 1 minute

return data;
}

Next Steps

Common Patterns

Search with Autocomplete

# Search as user types
GET /api/apps/blog-app/datatables/users/data/?\
username_startswith=joh&\
_start=0&\
_end=10

Infinite Scroll

let page = 1;
const pageSize = 20;

async function loadMore() {
const start = (page - 1) * pageSize;
const end = page * pageSize;

const data = await fetchData({ _start: start, _end: end });
appendToList(data);
page++;
}
# Get counts for each category
GET /api/apps/shop-app/datatables/products/data/?status=active

# Then filter by category
GET /api/apps/shop-app/datatables/products/data/?\
status=active&\
category=electronics