Universal Query Language (UQL) parser designed for URLs.
Parses user-friendly query strings (like field=5&field2='text'|exists(extraField)
) into an AST or a MongoDB query.
Automatically extracts $controls
(e.g. $page=2
, $limit=100
, etc.) from the query string.
-
AST-based: Parse into an abstract syntax tree representing logical AND/OR conditions, comparison operators, and
exists(...)
. - Easy integration: Convert the AST to your own format or directly to a MongoDB query.
-
Configurable:
- Optional
checkField
callback to validate/authorize field names. - Optional
castValue
callback to cast raw values to specific types (e.g.boolean
,Date
, etc.).
- Optional
-
Quoted strings: Supports explicit quoting via single
'...'
or double"..."
. -
Handles
=
,!=
,>
,>=
,<
,<=
,exists(...)
,!exists(...)
, grouping with parentheses, logical AND (&
) and OR (|
).
# with npm
npm install @prostojs/uql
# or with pnpm
pnpm add @prostojs/uql
import { UqlParser } from "@prostojs/uql";
const parser = new UqlParser();
// e.g. "field=5&field2='hello'|exists(extraField)"
const result = parser.parse("(field>5|field2='hello')&exists(extraField)&$page=2");
console.log(result.controls);
// { page: 2 }
console.log(JSON.stringify(result.ast, null, 2));
// Example AST:
// {
// "type": "and",
// "items": [
// {
// "type": "or",
// "items": [
// {
// "type": "condition",
// "field": "field",
// "operator": "gt",
// "value": 5
// },
// {
// "type": "condition",
// "field": "field2",
// "operator": "eq",
// "value": "hello"
// }
// ]
// },
// {
// "type": "condition",
// "field": "extraField",
// "operator": "exists"
// }
// ]
// }
The parser returns an object with:
-
ast
: The parsed abstract syntax tree. -
controls
: A record of the “control” parameters (any top-level key that starts with$
).
By default, the parser:
- Parses numeric-looking values (e.g.
123
) as numbers. - Leaves everything else as strings.
- An empty value (
field=
) is interpreted as""
(empty string).
If you want your own casting logic (like converting "true"
→ true
), pass a castValue
function:
import { UqlParser } from "@prostojs/uql";
const parserWithCast = new UqlParser({
castValue(fieldName, operator, rawValue) {
if (rawValue === "true") return true;
if (rawValue === "false") return false;
// fallback
return rawValue;
},
});
const result = parserWithCast.parse("flag=true&flag2=false");
console.log(JSON.stringify(result.ast, null, 2));
// {
// "type": "and",
// "items": [
// {
// "type": "condition",
// "field": "flag",
// "operator": "eq",
// "value": true
// },
// {
// "type": "condition",
// "field": "flag2",
// "operator": "eq",
// "value": false
// }
// ]
// }
You can validate or authorize field names by providing a checkField
callback:
const parserWithFieldCheck = new UqlParser({
checkField(fieldName) {
if (!["allowedField", "someOther"].includes(fieldName)) {
throw new Error(`Field ${fieldName} is not allowed!`);
}
return true;
},
});
try {
parserWithFieldCheck.parse("secret=123");
} catch (error) {
console.error(error.message);
// Field secret is not allowed!
}
This package also ships with a helper uqlToMongoQuery()
(in @prostojs/uql/mongo
) to convert the resulting AST into a Mongo query object:
import { UqlParser } from "@prostojs/uql";
import { uqlToMongoQuery } from "@prostojs/uql/mongo";
const parser = new UqlParser();
const { ast, controls } = parser.parse("(field>5|field2='hello')&exists(extraField)");
if (ast) {
const mongoQuery = uqlToMongoQuery(ast);
console.log(JSON.stringify(mongoQuery, null, 2));
// {
// "$and": [
// {
// "$or": [
// { "field": { "$gt": 5 } },
// { "field": "hello" }
// ]
// },
// {
// "extraField": { "$exists": true }
// }
// ]
// }
}
-
field=123
→ AST:{"type":"condition","field":"field","operator":"eq","value":123}
-
field='123'
→value: "123"
(explicit string) -
!exists(field)
→{"type":"condition","field":"field","operator":"exists","negateExists":true}
-
field>10&other!='foo'
- →
$and
of{"field":{"$gt":10}}
and{"other":{"$ne":"foo"}}
(if you convert to Mongo).
- →
MIT © 2025 Artem Maltsev and Contributors.