What Qgjv Actually Is
Qgjv is a command-line utility for validating JSON Schema documents against actual JSON data. It runs as a standalone binary and supports Draft-07 plus several newer drafts through a plugin system. The short version: you give it a schema file and a data file, and it tells you whether the data conforms, spits out errors with exact paths, and exits with a non-zero code if anything is off. I use it mainly in CI pipelines and validation scripts where I need a deterministic pass/fail result without importing a 40-megabyte JavaScript dependency tree. It has been reliable enough that I stopped reaching for python-jsonschema after writing a custom wrapper around Qgjv for a migration tool about two years ago.
Getting Started With Qgjv
The first thing most people miss is that Qgjv expects the schema to declare its $schema URI explicitly. If your schema is missing it or points to an old draft, Qgjv will still run but may validate against different rules than you intended. Always start every schema you hand to it with a clear $schema line pointing at the draft you actually want. You can download Qgjv from its official repository on GitHub. The releases page contains compiled binaries for Linux x64, macOS arm64, and Windows x64. Grab the one matching your target platform, chmod +x it if you are on Unix, and place it somewhere on your PATH. That is it. No npm install, no virtualenv, just a single binary. Once installed, a basic validation command looks like this:
qgjv validate --schema ./schema.json --data ./input.json If the data validates, Qgjv exits silently with code 0. If there are violations, it prints each error with a pointer path and exits with code 1. The output format is JSON by default, which makes piping it into other tools straightforward.
Advanced Usage and Common Pitfalls
One thing beginners consistently get wrong is how Qgjv handles nullable fields. If your schema uses "type": ["string", "null"] and your input contains an actual null value, Qgjv validates it correctly. But if you use "nullable": true alongside a typed field in Draft 2019-09 mode, behavior changes depending on how the schema was authored. I ran into this when a teammate handed me a schema that mixed Draft-07 style type arrays with Draft 2019-09 syntax. Qgjv accepted it without complaint and then produced confusing error messages about type mismatches that did not match the schema on paper. The fix was to normalize every schema through a draft-specific preprocessor before feeding it into Qgjv. I now run a small Python script that rewrites all $schema declarations and restructures nullable fields to match the target draft before validation begins. Another edge case involves recursive schemas and deeply nested data. Qgjv does support recursion, but the default recursion limit can be hit on schemas that model things like org charts or file trees. When I hit that wall, I bumped the limit using the --max-depth flag and set it to something reasonable like 64. Without that flag, the tool silently truncates its traversal and returns a false positive for deeply nested structures. That one cost me about an afternoon of debugging before I realized the validator was just giving up partway through. Performance is generally solid. For a typical API request schema under 500 lines validating payloads around 5 KB, a single Qgjv invocation takes roughly 30 to 80 milliseconds on a modern laptop. Batch processing hundreds of files in a loop is fast enough that I do not bother with parallelization for anything under a thousand inputs. Beyond that, I switch to running multiple Qgjv instances across available CPU cores using a simple GNU parallel command. A test suite with about 2,000 schema-data pairs dropped from roughly 45 minutes in serial to about six minutes with eight cores.
Where Qgjv Falls Short
It is not a perfect tool. The error messages are accurate but dense. They give you the JSON Pointer path and the keyword that failed, but they do not explain why in plain language. If you are handing results to non-technical stakeholders, you will need a translation layer. I wrote a small post-processor that takes Qgjv output and maps common error patterns into human-readable sentences, and it cut down support tickets significantly. Another limitation is that Qgjv does not currently support JSON Schema 2020-12 fully. It covers most features, but certain keywords like prefixItems and unevaluatedProperties behave differently than the specification describes in edge cases involving $ref cycles. If your schemas rely heavily on those, you will run into subtle inconsistencies. I work around this by keeping a forked version of Qgjv patched with community pull requests and rebuilding the binary from source when a critical fix lands upstream. For projects that need full 2020-12 compliance with extensive use of $dynamicRef and complex ref resolution, jsonschema in Python remains the more complete option despite the heavier dependency footprint. If your validation needs are straightforward or you are locked into a no-dependency environment, Qgjv is worth the initial setup time. It does exactly what it says without pulling in an entire runtime.