CLI tool
yamafaktory/jql avatar
yamafaktory/jql

jql: a JSON Query Language CLI with its own syntax, not a jq clone

A JSON Query Language CLI tool. Separators Group separator Group separators build up an array from sub-queries.

1,682 stars32 forksRustApache-2.0

At a glance

What is it?
jql is a Rust CLI that selects, reshapes and validates JSON with a token-based query language. It is fast to install, deliberately small, and explicitly not trying to be jq.
Who is it for?
Adopt jql if you want a small, single-binary JSON selector with a query syntax you can learn in an afternoon, and if you are comfortable with the project's stated non-goal of not aligning with jq. Do not adopt it if your pipelines already depend on jq programs, jq modules or jq's expression language, because jql gives you no migration path and its maintainer says none is planned.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What jql solves, and who it is actually for

jql answers a narrow question: given a JSON document on stdin or in a file, produce a smaller JSON document, without writing a script. The README frames this as a philosophy rather than a feature list: be fast, stay lightweight, keep features as simple as possible, avoid redundancy, provide meaningful error messages, and eat JSON as input, process, output JSON back. That last point is the one that shapes everything else. Output is JSON, so a jql invocation can sit in the middle of a pipeline and feed another JSON consumer.

The intended user is someone who reaches for a shell one-liner often enough to care about startup time, but who does not want to learn or maintain a full expression language. If you have ever piped a Kubernetes manifest, a Terraform plan or a Docker log line through a query tool just to pull out one field, you are the target reader. The project ships as a Rust workspace with a binary crate and two library crates, jql-parser and jql-runner, so the parsing and evaluation logic is also usable outside the CLI.

It is not a general-purpose data processor. There is no arithmetic, no string interpolation, no user-defined function, and no plan to add them. The README states the non-goal plainly: there is no plan to align jql with jq or any other similar tool. Treat that as a design commitment, not a temporary gap.

Tokens, separators and selectors: how a jql query is evaluated

A jql query is a sequence of tokens applied to the input document. The README describes the input as a query made of tokens, and the first constraint is lexical: key selectors must be double-quoted to stay compliant with RFC 8259, which permits any string as a JSON key. That means a query such as "a" is a key selector, and because the shell also treats double quotes specially, the README says a query must be enclosed in single quotation marks or have every inner double quote escaped.

Tokens compose in a readable order. A key selector descends into an object. A group separator, written as a comma-separated list of sub-queries, builds an array from those sub-queries, so '"a","b","c"' against {"a":1,"b":2,"c":3} yields [1,2,3]. Array and object selectors accept explicit index lists, so '[2,1]' against [1,2,3] yields [3,2], and ranges accept natural, reversed, open-lower and open-upper forms such as [0:2], [2:0], [:2] and [0:]. The same range grammar applies to objects, where indices refer to key order.

Operators add the interesting behaviour. '..' flattens nested arrays and objects; applied to a deeply nested array it returns the leaves in order, and applied to a nested object it returns a flat object whose keys are dot-joined paths such as "b.d.e.f". '@' returns the keys of an object or the indices of an array, and returns other primitives as is. '|>' applies the following tokens in parallel to each element of an array, and '<|' stops that parallelization, which is how you fan out over a list and then collapse back to a single value. '!' truncates the result to a primitive or an empty array/object.

The lens selector is the most distinctive piece. Written as '|={...}', it filters an array of objects by a combination of selectors and optional literal values, where a value can be a boolean, null, number or string. In the README example, '|={"b""d"=2, "c"}' keeps the objects where the nested b.d path equals 2, plus the object that has a c key. That is a filter and a projection in one token, and it is the closest jql comes to a WHERE clause.

Installing jql and running a first query

Distribution is broad for a small CLI. The README lists Alpine, Arch, Fedora, FreeBSD, Homebrew, Nix, openSUSE, Cargo and Cargo Binstall, plus manual downloads of compiled binaries from the GitHub releases page. If you already have a Rust toolchain, the shortest path is cargo:

bash
cargo install jql

After that, jql should be on your PATH and jql --version should print the installed version. For a first real query, take the README's group separator example. Save the input below as input.json:

json
{ "a": 1, "b": 2, "c": 3 }

Then run a query that pulls three keys into an array. Note the single quotes around the query, which keep the shell from eating the double quotes that jql requires on key selectors:

bash
jql '"a","b","c"' input.json

The expected output is [1, 2, 3]. From there, the two flags you will reach for most are -i and -s. The README says output is pretty printed by default and -i, --inline disables that, which matters when the result feeds another program rather than a human. The -s, --stream flag reads a stream of JSON data line by line, and the README is careful about what it is for: it is about processing JSON output streamed line by line, such as Docker logs with --follow, and it is explicitly not an option for reading incomplete streamed content or a very large input. If you want to check a file without querying it, -v, --validate returns a matching exit code based on the validity of the JSON content or file provided, which makes it usable directly in a shell conditional.

Where jql runs out of road

The honest limitation is the query language itself. There is no arithmetic, no comparison operators outside the lens selector, no string functions, no variables, and no way to define reusable queries other than storing one in a file and passing it with -q, --query <FILE>. If your task is to sum a field across an array, reformat a timestamp, or join two documents, jql cannot express it, and no amount of token composition will get you there. The README's non-goal section makes this a deliberate boundary rather than a backlog item.

The stream flag is the second place where expectations can outrun the implementation. Because -s is scoped to line-by-line JSON output, feeding it a single multi-megabyte JSON document will not work the way a reader might assume from the word stream. The README says so directly, but the flag name invites the mistake.

Finally, the repository's own tooling suggests a project with a small surface area and a correspondingly small contributor base. The justfile exposes audit, clippy, fmt, test, test-bin, test-parser, test-runner and two fuzz targets, fuzz_parser and fuzz_lazy, and the workspace lints deny unsafe code and clippy's all group. That is a tight, well-instrumented codebase, but the README does not document a compatibility policy for query syntax across major versions, and the version numbers in the repository move in the 8.x and 9.x range. Pin what you install.

jql compared with jq and with plain Python

The obvious alternative is jq, and the difference is not cosmetic. jq gives you a full expression language with pipes, functions, conditionals, arithmetic and a module system; jql gives you a fixed token grammar with no user-defined logic. The practical consequence is that a jq program can be arbitrarily complex and a jql query cannot. If your team already has jq filters checked into a repository, jql offers no migration path and the maintainer states there is no plan to align the two tools. Choosing jql means rewriting those filters in a language that is smaller by design.

The second alternative is a short Python or Node script using the standard JSON library. That approach is unbounded in what it can express and needs a runtime on every machine that runs it. jql's trade is the opposite: a compiled binary with no interpreter dependency and a query language you can hold in your head, at the cost of expressiveness. For a CI step that extracts one field from a manifest, the binary is the lighter dependency. For anything involving computation, the script wins and jql is the wrong tool.

Licence, maintenance and the cost of upgrading

The workspace Cargo.toml declares the package licence as MIT OR Apache-2.0, and the repository root carries both LICENSE-MIT and LICENSE-APACHE files. That dual arrangement is the Rust ecosystem default and is permissive in both directions, but the choice between the two is yours to make in your own distribution paperwork; this is not legal advice, and if you redistribute jql inside a product you should read both files rather than assume.

On maintenance, the last push to the default branch was on 2026-03-18, and the most recent releases listed are jql-v8.1.0, jql-v8.1.1 and jql-v8.1.2, all dated 2026-03-18. The repository is not archived. The changelog is maintained with git-cliff, and the justfile has a changelog target that prepends unreleased entries to CHANGELOG.md, so the upgrade path is at least documented in the repository rather than only in release notes. The workspace pins rust-version = 1.88, which is the practical floor if you build from source rather than installing a binary.

The upgrade cost is the usual one for a CLI with a custom grammar: a query that parses today may parse differently after a major version bump, and the README does not describe a deprecation window. The mitigation available in the repository is CHANGELOG.md plus the -v validate flag, which lets you assert that an input still parses before you trust a query against it.

Editorial conclusion

Adopt jql if you want a small, single-binary JSON selector with a query syntax you can learn in an afternoon, and if you are comfortable with the project's stated non-goal of not aligning with jq. Do not adopt it if your pipelines already depend on jq programs, jq modules or jq's expression language, because jql gives you no migration path and its maintainer says none is planned. Before committing, verify two things against your own data: that a lens query such as '|={"b""d"=2, "c"}' expresses the filter you actually need, and that the -s stream flag matches the shape of the logs you intend to feed it. The README does not document rollback or output-format stability across major versions, so pin the version you install and read CHANGELOG.md before upgrading.

Frequently asked questions

What does JQL stand for in yamafaktory/jql?

The README expands it as JSON Query Language, and the tool is pronounced like jackal. It is a CLI that takes JSON as input, processes a token-based query, and outputs JSON.

Is jql's query language similar to SQL?

No. jql is a token grammar of selectors, separators and operators rather than a declarative query language, and the README states there is no plan to align it with jq or any other similar tool. The closest thing to a filter is the lens selector, which matches array elements against paths and literal values.

What is the JSON query language and how does it work in jql?

A query is a sequence of tokens applied to the input document. Key selectors must be double-quoted per RFC 8259, group separators build an array from sub-queries, and operators such as '..', '@', '|>', '<|' and '!' flatten, list keys, fan out, collapse and truncate.

Can you give an example of a jql query?

Against the input {"a":1,"b":2,"c":3}, the query '"a","b","c"' returns [1, 2, 3]. The README also shows '[2,1]' against [1,2,3] returning [3, 2], and '..' flattening nested objects into dot-joined keys.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Community notes

Community notes