Recipes

Server-side Filtering with React Query Builder

Send filter rules to an API, validate them, and return matching rows.

This demo uses a mock service

This interactive demo simulates the backend or AI provider. In your app, validate requests on your server and keep API keys and access checks there.

What this recipe builds

  • A predictable request and response format.
  • Backend validation demonstrated with a replaceable mock API.
  • Each request includes a page number and page size, and limits how complex the filter can be.
Loading the interactive recipe demo...

Install and imports

Install / imports
npm install @vojtechportes/react-query-builder

Fields and initial query

Typed field configuration
const fields: IBuilderFieldProps[] = [
{ field: 'status', label: 'Status', type: 'LIST', value: statuses },
{ field: 'amount', label: 'Amount', type: 'NUMBER' },
];
const initialQuery: DenormalizedQuery = [
{ type: 'GROUP', value: 'AND', isNegated: false, children: [] },
];

Builder implementation

React implementation
import '@vojtechportes/react-query-builder/styles.css';
const [query, setQuery] = useState(initialQuery);
const [rows, setRows] = useState<Order[]>([]);
const search = async () => {
const response = await fetch('/api/orders/search', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ query, page: 1, pageSize: 50 }),
});
setRows((await response.json()).rows);
};
return (
<>
<Builder fields={fields} data={query} onChange={setQuery} />
<button onClick={search}>Search</button>
<Results rows={rows} />
</>
);

Validate the API request

Validate the API request
// Example server handler: this code does not run on the static site.
const request = validateSearchRequest(await req.json(), {
allowedFields: ['status', 'amount'],
allowedOperators: ['EQUAL', 'LARGER_EQUAL'],
maxDepth: 4,
maxRules: 20,
maxPageSize: 100,
});
const scopedQuery = addRequiredScope(request.query, {
tenantId: session.tenantId,
userId: session.userId,
});
return Response.json(await orderService.search(scopedQuery, request.page));

Validation and safety

  • Treat every filter sent by the browser as untrusted. It must never decide which records a user is allowed to access.
  • Check supported fields, operators, and value types, and reject filters that are too large or deeply nested.
  • Always add user or organization access rules on the server.

Production notes

  • Return structured validation errors without echoing secrets.
  • Log rejected filter shape and request ids, not sensitive values.

Related guides

Frequently asked questions

What should the server check before applying a filter?

Check every field, operator, and value. Also reject filters with too many nested groups or rules, and requests outside your pagination limits.

Is a locked rule enough to protect restricted records?

No. A locked rule can explain the filter in the UI, but the server must still decide which records the user can access.

© Vojtěch Václav Porteš 2026 - All library contents are available under the MIT license.
Loading privacy preferences...