A lightweight query language engine for building filters without writing complex SQL or ORM queries.
FlexQL lets you write readable and compact queries, which are parsed and converted into database-specific formats like SQL or Sequelize. Instead of manually building queries, you can write:
username==heja;age>18,status==active
Which means:
(username == "heja" AND age > 18) OR (status == "active")
⚠️ Note: Do not use spaces in the query string as separators. Use;for AND and,for OR.
- 🧠 Readable syntax — intuitive query format
- 🔀 Flexible separators —
;= AND,,= OR - 🔒 Safe — parameterized output to prevent SQL injection
- 🛡️ Column protection — restrict or block access to sensitive columns
- ⚙️ Adapter-based — SQL, Sequelize, MongoDB (coming), Elasticsearch (coming)
- 🧱 Lexer → Parser → Adapter — modular and extensible
- 🧪 Type-validated — automatically checks numbers, strings, booleans
- 🧰 Easy to extend — add new adapters or operators quickly
Query String
↓
Lexer → tokens
↓
Parser → AST (Abstract Syntax Tree)
↓
Adapter → SQL / Sequelize / MongoDB
username==heja;age>18,status==active
{
"type": "OR",
"children": [
{
"type": "AND",
"children": [
{ "field": "username", "operator": "==", "value": "heja" },
{ "field": "age", "operator": ">", "value": 18 }
]
},
{ "field": "status", "operator": "==", "value": "active" }
]
}FlexQL builds a structured query, not just parses a string.
username==heja;age>18;country==NL
{
type: 'sql',
payload: {
conditions: 'WHERE username = ? AND age > ? AND country = ?',
values: ['heja', 18, 'NL']
}
}Usage:
import { FlexQL } from "flexql";
import db from "./db"; // your database connection
const result = FlexQL.parse('username==heja;age>18;country==NL', {
adapter: "sql",
});
const rows = await db.query(
`SELECT * FROM users ${result.payload.conditions}`,
result.payload.values,
);username==heja;age>18,status==active
{
type: 'sql',
payload: {
conditions: 'WHERE username = ? AND age > ? OR status = ?',
values: ['heja', 18, 'active']
}
}Usage:
const result = FlexQL.parse('username==heja;age>18,status==active', {
adapter: "sql",
});
const users = await db.query(
`SELECT * FROM users ${result.payload.conditions}`,
result.payload.values,
);created_at>=2025-01-01;created_at<=2025-12-31;active==true
{
type: 'sql',
payload: {
conditions: 'WHERE created_at >= ? AND created_at <= ? AND active = ?',
values: ['2025-01-01', '2025-12-31', true]
}
}Usage:
const result = FlexQL.parse(
'created_at>=2025-01-01;created_at<=2025-12-31;active==true',
{ adapter: "sql" },
);
const logs = await db.query(
`SELECT * FROM audit_logs ${result.payload.conditions} ORDER BY created_at DESC`,
result.payload.values,
);✅ Safe and parameterized — values are never interpolated into the query string.
username==heja,country==NL;score>90,rank>=5;active==true,verified==true
{
type: 'sequelize',
payload: {
conditions: {
[Op.and]: [
{
[Op.or]: [
{ username: 'heja' },
{ country: 'NL' }
]
},
{
[Op.or]: [
{ score: { [Op.gt]: 90 } },
{ rank: { [Op.gte]: 5 } }
]
},
{
[Op.or]: [
{ active: true },
{ verified: true }
]
}
]
}
}
}Usage:
import { FlexQL } from "flexql";
import { User } from "./models";
const result = FlexQL.parse(
'username==heja,country==NL;score>90,rank>=5;active==true,verified==true',
{ adapter: "sequelize" },
);
const users = await User.findAll({
where: result.payload.conditions,
});role==admin,role==moderator;banned==false;last_login>=2025-06-01
{
type: 'sequelize',
payload: {
conditions: {
[Op.and]: [
{
[Op.or]: [
{ role: 'admin' },
{ role: 'moderator' }
]
},
{ banned: false },
{ last_login: { [Op.gte]: '2025-06-01' } }
]
}
}
}Usage:
// req.query.filter = 'role==admin,role==moderator;banned==false;last_login>=2025-06-01'
const result = FlexQL.parse(req.query.filter, { adapter: "sequelize" });
const admins = await User.findAll({
where: result.payload.conditions,
order: [["last_login", "DESC"]],
});category==electronics;price>=100;price<=999;in_stock==true
{
type: 'sequelize',
payload: {
conditions: {
[Op.and]: [
{ category: 'electronics' },
{ price: { [Op.gte]: 100 } },
{ price: { [Op.lte]: 999 } },
{ in_stock: true }
]
}
}
}Usage:
const result = FlexQL.parse(
'category==electronics;price>=100;price<=999;in_stock==true',
{ adapter: "sequelize" },
);
const products = await Product.findAndCountAll({
where: result.payload.conditions,
limit: 20,
offset: page * 20,
});FlexQL lets you restrict which columns can be queried and which operators are allowed per column. This is useful for preventing access to sensitive fields and enforcing query rules on the server side.
| Option | Type | Description |
|---|---|---|
exclude |
string[] |
Columns that cannot be queried at all |
rules |
{ [column: string]: Operator[] } |
Allowed operators per column |
Prevent querying sensitive columns like password, token, or secret:
const result = FlexQL.parse('username==heja;password==1234', {
adapter: "sql",
columnProtect: {
exclude: ["password", "token"],
},
});
// ❌ Throws: "Access to protected column is not allowed."Allow only specific operators for a column. For example, email should only support exact match, while age should only support comparison operators:
const result = FlexQL.parse('age>=30;username==heja', {
adapter: "sql",
columnProtect: {
rules: {
age: [">", "<", "<=", ">="],
email: ["=="],
},
},
});If a disallowed operator is used:
FlexQL.parse('email>a', {
adapter: "sql",
columnProtect: {
rules: {
email: ["=="],
},
},
});
// ❌ Throws: "Operator '>' is not allowed for column 'email'. Allowed: =="const flexQl = new FlexQL();
const result = flexQl.parse(
'age>=30;username==heja,username==admin,country==NL;score>80,rank>=10;active==true,verified==true',
{
separators: { and: ";", or: "," },
adapter: "sql",
columnProtect: {
exclude: ["password"],
rules: {
age: [">", "<", "<=", ">="],
},
},
},
);| Operator | Description |
|---|---|
== |
Equal |
!= |
Not equal |
> |
Greater than |
< |
Less than |
>= |
Greater than or equal |
<= |
Less than or equal |
| Element | Description | Examples |
|---|---|---|
| Identifier | Column / field | username, age |
| Operator | Comparison | ==, !=, >, <, >=, <= |
| Logic | Combine conditions | ; = AND, , = OR |
| Value | Match value | heja, 18, true |
username==test;age>10
username==test,status==false
age>=18;(country==TR,country==DE);premium_user==true
score>50,rank>=3;active==true,verified==true
username==heja,country==NL;score>90,rank>=5;active==true,verified==true;created_at>=2025-01-01;updated_at<=2025-10-01,last_login>=2025-09-01
🚧 Coming Soon — FlexQL will be available on npm shortly. Stay tuned!
npm install flexqlimport { FlexQL } from "flexql";
const query = 'username==heja;age>18;country==NL';
const result = FlexQL.parse(query, { adapter: "sequelize" });
console.log(result.payload.conditions);- ✅ Readable and compact queries
- 🧱 Works across multiple databases
- 🧠 Logical precedence support — AND > OR
- 🔒 Safe parameterized output
- 🛡️ Column-level access control — exclude or restrict operators per column
- 🌍 Portable architecture via adapters
- 🧩 Modular & extensible
- Admin dashboards with dynamic filters
- API query parameter parsing
- ORM-independent rule engines
- Safe filtering for user input
- Multi-tenant apps with column-level access control
- SQL adapter
- Sequelize adapter
- Column protection (exclude + operator rules)
- MongoDB adapter
- Elasticsearch adapter
- Nested query support
- Type inference & validation
- Query optimizer
- FlexQL Playground
MIT License © 2025 Heja "xeja" Arslan
