Let users filter your data with simple, readable expressions.
age > 18 AND status = "active"
Filtron parses human-friendly filter strings into structured queries you can use anywhere — SQL databases, in-memory arrays, or your own custom backend.
Building filter functionality is tedious. You need to:
- Parse user input safely (no "SQL injection")
- Handle different operators, boolean logic, nested conditions
- Generate the right output for your data layer
Filtron handles the hard parts. You get a well-tested parser that turns readable expressions into type-safe, structured queries.
Since filtering usually is a synchronous operation in real-time APIs, performance is one of its main priorities. We benchmark every change and continuously look for ways to improve it.
Input:
price < 100 AND category : ["electronics", "books"] AND inStock
Output (SQL):
WHERE (price < $1 AND category IN ($2, $3) AND inStock = $4)
-- params: [100, "electronics", "books", true]Output (JavaScript):
items.filter(item =>
item.price < 100 &&
["electronics", "books"].includes(item.category) &&
item.inStock
)- Search UIs — Let users type natural filter expressions instead of building complex form interfaces
- API query parameters — Accept
?filter=price < 50 AND rating >= 4in your REST endpoints - Admin dashboards — Give power users flexible filtering without writing raw SQL
- Real-time streams — Filter WebSocket or event data on the fly
npm install @filtron/core
# Or directly add SQL or JavaScript helpers
npm install @filtron/sql # For SQL WHERE clauses
npm install @filtron/js # For in-memory array filteringimport { parse } from "@filtron/core";
const result = parse('age > 18 AND status = "active"');
if (result.success) {
// result.ast contains the parsed query
} else {
// result.error contains what went wrong
}import { parse } from "@filtron/core";
import { toSQL } from "@filtron/sql";
const result = parse('age > 18 AND status = "active"');
if (result.success) {
const { sql, params } = toSQL(result.ast);
// sql: "(age > $1 AND status = $2)"
// params: [18, "active"]
await db.query(`SELECT * FROM users WHERE ${sql}`, params);
}import { parse } from "@filtron/core";
import { toFilter } from "@filtron/js";
const result = parse('age > 18 AND status = "active"');
if (result.success) {
const filter = toFilter(result.ast);
const users = [
{ name: "Alice", age: 25, status: "active" },
{ name: "Bob", age: 16, status: "active" },
];
users.filter(filter);
// => [{ name: "Alice", age: 25, status: "active" }]
}Filtron supports a rich set of operators for building expressive queries:
age > 18
age >= 21
price < 100
score <= 4.5
status = "active"
role != "guest"
age > 18 AND verified
role = "admin" OR role = "moderator"
NOT suspended
(role = "admin" OR role = "mod") AND active
Use parentheses to control precedence and group conditions:
(role = "admin" OR role = "moderator") AND active
((a AND b) OR (c AND d)) AND verified
Check if a field is present:
email?
profile EXISTS
name ~ "john"
description ~ "sale"
Match against a list of values:
status : ["pending", "approved", "rejected"]
category : ["books", "electronics"]
age = 18..65
temperature = -10..20
user.profile.age >= 18
order.shipping.address.country = "PT"
| Operator | Meaning | Example |
|---|---|---|
=, : |
Equal | status = "active" |
!=, !: |
Not equal | role != "guest" |
>, >=, <, <= |
Comparison | age >= 18 |
~ |
Contains | name ~ "john" |
?, EXISTS |
Field exists | email? |
.. |
Range | age = 18..65 |
: [...] |
One of | status : ["pending", "active"] |
AND, OR, NOT |
Boolean logic | verified AND (admin OR moderator) |
Use parseOrThrow if you prefer exceptions over result objects:
import { parseOrThrow } from "@filtron/core";
try {
const ast = parseOrThrow('age > 18 AND status = "active"');
} catch (error) {
console.error(error.message);
}Filtron is a monorepo with focused packages:
| Package | Version | Bundle Size | Description |
|---|---|---|---|
| @filtron/core | Core parser — parses expressions into an AST | ||
| @filtron/sql | Generates parameterized SQL WHERE clauses | ||
| @filtron/js | Creates filter functions for JavaScript arrays | ||
| @filtron/benchmark | Benchmark utilities and performance tests |
The parser produces a typed AST you can traverse for custom use cases:
parse('age > 18 AND status = "active"')
// Returns:
{
type: "and",
left: {
type: "comparison",
field: "age",
operator: ">",
value: { type: "number", value: 18 }
},
right: {
type: "comparison",
field: "status",
operator: "=",
value: { type: "string", value: "active" }
}
}Full TypeScript types are exported for building custom AST consumers.
Filtron currently uses a hand-written lexer and recursive descent parser and has no runtime dependencies (~9 KB minified). We continuously monitor performance with CodSpeed to test out new optimizations.
The AST syntax is seen as stable and any changes follows strict semantic versioning (we for instance switched from a PEG parser to a hand-written recursive descent parser after the initial release).
| Query Complexity | Parse Time | Throughput |
|---|---|---|
| Simple | ~90-250ns | 4-11M ops/sec |
| Medium | ~360-870ns | 1.1-2.8M ops/sec |
| Complex | ~0.9-1.5μs | 650K-1.1M ops/sec |
The helper packages add minimal overhead:
| Package | Additional Overhead |
|---|---|
| @filtron/js | ~0.2μs |
| @filtron/sql | ~0.3μs |
Run benchmarks locally: bun run bench in each package directory.
See the Contributing Guide for development setup and guidelines.
The query syntax was strongly inspired by dumbql.
MIT — See LICENSE