@softwareone-platform/rql-client
v0.1.1
Published
TypeScript library for building and evaluating RQL (Resource Query Language) queries, with ESLint integration.
Readme
@softwareone-platform/rql-client
A TypeScript library for building and evaluating RQL (Resource Query Language) queries. Combines the former @swo/rql-client (query builder + ESLint rules) and @swo/rql-parser (RQL expression evaluator) into a single package.
Installation
npm install @softwareone-platform/rql-clientEntry points
| Import | Purpose |
| ------------------------------------------------ | ------------------------------------------------------------ |
| @softwareone-platform/rql-client | Query builder (RqlQuery, operators, helpers) + test() |
| @softwareone-platform/rql-client/parser | test(expression, data) — parser-only subpath |
| @softwareone-platform/rql-client/eslint-rules | ESLint rules for the immutable RqlQuery API |
test is re-exported from the root for convenience, so import { RqlQuery, test } from '@softwareone-platform/rql-client' works out of the box. The ./parser subpath remains available for consumers that only need the evaluator and want the smallest possible dependency surface.
Building queries
import { RqlQuery } from '@softwareone-platform/rql-client';
const query = new RqlQuery()
.expand('id', 'name', 'email')
.filter(new RqlQuery().operators.eq('status', 'active'))
.orderBy(['createdAt', 'desc'])
.paging(0, 20);
query.toString();
// select=id,name,email&eq(status,"active")&order=-createdAt&offset=0&limit=20Evaluating RQL expressions
The parser subpath exposes a single test(expression, data) function that checks whether a JavaScript object satisfies an RQL expression. It parses the RQL string into an AST and compiles it into a predicate at runtime, returning true / false. On any parsing or evaluation error the function logs the error and returns false — it never throws.
import { test } from '@softwareone-platform/rql-client/parser';
const user = { name: 'John', status: 'Active', age: 20 };
test('(name=John)&status=Active', user); // true
test('status=Active&age>18', user); // true
test('status=Pending', user); // falseSupported syntax
Both the shorthand form (e.g. status=Active) and the function form (e.g. eq(status,Active)) are supported.
| Category | Operators | Example |
| ----------- | -------------------------------------------------------------------- | ------------------------------------------------------- |
| Equality | = / eq, != / ne | status=Active, ne(status,Active) |
| Comparison | > / gt, >= / ge, < / lt, <= / le | age>18, ge(age,18) |
| Logical | & / and, \| / or, ! / not | status=Active&age>18, !verified=true |
| Membership | in=(...) / in, out=(...) / out | in(country,(US,CA)), country in=(US,CA) |
| Pattern | like, ilike (case-insensitive); * = any chars, ? = one char | like(name,Jo*), ilike(code,a?c) |
| Collections | any(field,expr), all(field,expr) — operate on array fields | any(lines, id=A), all(lines, type=A) |
Nested object paths are supported using dot notation inside field names (e.g. user.profile.name=John). Values may be quoted ("..." / '...'), numeric, or boolean; escape characters inside quoted strings follow JSON conventions.
// Arrays with any / all
test('any(lines, id=A)', { lines: [{ id: 'A' }, { id: 'B' }] }); // true
test('all(lines, type=A)', { lines: [{ type: 'A' }, { type: 'A' }] }); // true
// Nested paths
test('user.profile.role=admin', { user: { profile: { role: 'admin' } } }); // true
// Wildcards (like / ilike)
test('like(name,Jo*)', { name: 'John' }); // true
test('ilike(code,a?c)', { code: 'ABC' }); // trueESLint rules
// eslint.config.js
import rqlRules from '@softwareone-platform/rql-client/eslint-rules';
export default [
{
plugins: { rql: rqlRules },
rules: {
'rql/no-unused-return': 'error',
'rql/no-clone-method': 'error',
},
},
];@typescript-eslint/utils is an optional peer dependency — only required when you enable the ESLint rules.
Development
npm install
npm run build # esbuild (ESM + CJS) + tsc (types)
npm test # jest
npm run typecheck # tsc --noEmitThe build pipeline (scripts/build.mjs) produces:
dist/
├── index.{mjs,cjs,d.ts} # query builder
├── parser/index.{mjs,cjs,d.ts} # RQL evaluator
└── eslint-rules/index.{mjs,cjs,d.ts} # ESLint rules