DuckDB Extension
JSON Schema
Validate JSON documents against a JSON Schema in DuckDB SQL. Check required fields and value types, or fill in defaults defined by your schema.
On this page
Check that JSON documents follow an agreed structure
A JSON Schema describes a document’s expected fields, types, and constraints. DuckDB can read JSON with different shapes; this extension adds an explicit check against the contract you choose.
Use it to check API payloads or incoming records before further processing. The validator targets JSON Schema draft-07 and can apply schema-defined defaults. Validation errors normally stop the query; the example shows how to turn invalid documents into a boolean result after checking the schema itself.
Example
Check two JSON documents
The schema requires an integer id. The first document passes; the second has a string where an integer is required. All sample data is included.
Tested with DuckDB 1.5.6. Browser examples use Haybarn-Wasm 1.5.6.
INSTALL json_schema FROM community;
LOAD json_schema;
WITH schema AS (
SELECT '{
"type": "object",
"properties": {"id": {"type": "integer"}},
"required": ["id"]
}'::JSON AS definition
), events(row_num, payload) AS (
VALUES
(1, '{"id": 42}'::JSON),
(2, '{"id": "oops"}'::JSON)
)
SELECT row_num, payload,
COALESCE(
TRY(json_schema_validate(definition, payload)), false
) AS valid
FROM events CROSS JOIN schema
ORDER BY row_num;
SET allow_extension_repositories = 'allowed';
CREATE EXTENSION REPOSITORY IF NOT EXISTS query_farm
WITH PREFIX 'https://duckdb-extensions.query.farm';
INSTALL json_schema FROM query_farm;
LOAD json_schema FROM query_farm;
WITH schema AS (
SELECT '{
"type": "object",
"properties": {"id": {"type": "integer"}},
"required": ["id"]
}'::JSON AS definition
), events(row_num, payload) AS (
VALUES
(1, '{"id": 42}'::JSON),
(2, '{"id": "oops"}'::JSON)
)
SELECT row_num, payload,
COALESCE(
TRY(json_schema_validate(definition, payload)), false
) AS valid
FROM events CROSS JOIN schema
ORDER BY row_num;
Expected result
| row_num | payload | valid |
|---|---|---|
| 1 | {"id":42} | true |
| 2 | {"id":"oops"} | false |
Validation raises an error for a nonconforming document. TRY turns that error into NULL; COALESCE makes the result false. Validate a schema separately before using this pattern with your own data, because TRY also catches errors in the schema. The validator targets JSON Schema draft-07.
Common tasks
- Check the schema itself Catch schema errors before validating a batch of documents.
- Validate a JSON column Count valid and invalid documents in an existing table.
- Apply default values Fill missing fields using defaults declared in the schema.
- Read JSON files into DuckDB See how JSON loading and schema validation fit together.
Deep Dive
Technical Details
Validation behavior
json_schema_validate returns TRUE for a valid document and raises an error for an invalid one. It does not return a structured list of validation failures. When validating a batch, check the schema itself with json_schema_validate_schema before wrapping document validation in TRY.
The validator targets JSON Schema draft-07. Check the keywords your schema uses, particularly if it was authored for a newer draft. External files and HTTP references are not resolved; include referenced definitions in the schema document.
Architecture
Internally this is a thin DuckDB scalar-function wrapper around two C++ libraries: pboettch/json-schema-validator for the validation engine and nlohmann/json for parsing. Both schema and data are passed as DuckDB JSON values (or struct literals, or strings cast with ::JSON). There is no ATTACH, no secret type, no network call — the validator runs locally against the in-query schema.
Four scalar functions cover the surface:
| Group | Functions | Returns |
|---|---|---|
| Validate | json_schema_validate, json_schema_validate_schema |
BOOLEAN |
| Enrich | json_schema_patch, json_schema_update |
JSON |
Schema features in scope match what draft-07 covers: type validation (string, number, integer, boolean, array, object, null), required, properties, numeric minimum / maximum / multipleOf, string minLength / maxLength / pattern / format, array minItems / maxItems / uniqueItems, and the default keyword used by json_schema_patch and json_schema_update.
Pairs with DuckDB’s JSON support
Schema-side validation is one piece; the rest of the JSON pipeline is plain DuckDB. Read JSON files with read_json_auto, pull out fields with json_extract_*, or cast strings with ::JSON — and feed any of those into json_schema_validate. See DuckDB JSON overview for the surrounding toolkit.
Compared to alternatives
- DuckDB
CHECKconstraints — if your data has a fixed shape, modeling it as proper columns plusCHECKis faster, clearer, and gives you better error messages. Reach for JSON Schema when the payload is genuinely contract-defined JSON and the storage stays opaque. - Application-layer validators (ajv / Pydantic / jsonschema) — these give detailed per-keyword failure reports and richer draft coverage. Pair them at the API boundary; use this extension as the SQL-side gate inside DuckDB so the database doesn’t depend on a Python service to enforce its contracts.
- Manual SQL predicates — handcrafted
WHEREs overjson_extract_*get unwieldy fast for non-trivial schemas. Pointingjson_schema_validateat a versioned schema file keeps the contract in one place.
Default-value enrichment
Use the schema’s declared default values to fill missing fields. json_schema_patch returns a JSON Patch (RFC 6902) array of add operations; json_schema_update applies them inline. Together they let you go from “incoming partial payload” to “fully populated record” in one SQL expression — no per-field COALESCE cascade, no application-layer enrichment code.
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
Enrich
Default-value handling. |
||
| json_schema_patch() | Object type: Scalar function |
Compute the JSON Patch (RFC 6902) diff that would bring json_data into conformance with the default values declared by the schema.
|
| json_schema_update() | Object type: Scalar function |
Apply the schema's default values inline.
|
|
Validate
Conformance checks. json_schema_validate checks a value against a schema; json_schema_validate_schema checks whether a schema can be loaded. Both return TRUE on success and raise on failure. Wrap with COALESCE(TRY(...), false) when a boolean filter is required. |
||
| json_schema_validate() | Object type: Scalar function | Validate a JSON value against a JSON Schema. |
| json_schema_validate_schema() | Object type: Scalar function | Check that the validator can load a JSON Schema. |
No extension contents match that search.
API Reference
Function Reference
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Validate one document
SELECT json_schema_validate('{ "$schema": "https://json-schema.org/draft-07/schema", "type": "object", "properties": { "id": {"type": "integer"}, "name": {"type": "string"} }, "required": ["id"]}', {'id': 5, 'name': 'George'}) AS valid;-- TRUEThe schema document follows the JSON Schema specification. See json_schema_validate.
Validate a column
Most real workloads point validation at a JSON column instead of a literal:
SELECT COUNT(*) AS total, COUNT(*) FILTER (WHERE COALESCE(TRY(json_schema_validate(:schema, payload)), false)) AS valid, COUNT(*) FILTER (WHERE NOT COALESCE(TRY(json_schema_validate(:schema, payload)), false)) AS invalidFROM events;json_schema_validate returns TRUE for valid data and raises an error for invalid data. TRY turns errors into NULL; COALESCE(..., false) counts errors and SQL NULL inputs as invalid. Validate the schema once outside TRY before processing rows, because TRY also catches invalid-schema errors. Here :schema and :event_schema stand for schema values supplied by your application.
Lint the schema before deploying
Before pushing a schema to production, verify it’s well-formed:
SELECT json_schema_validate_schema('{ "$schema": "https://json-schema.org/draft-07/schema", "type": "object", "properties": { "id": { "type": "integer" } }}') AS schema_ok;-- TRUEjson_schema_validate_schema checks whether the validator can load a schema, raising on errors. It is not exhaustive meta-schema validation and may accept unsupported keyword values. To find entries that fail to load without aborting the registry scan:
SELECT name, versionFROM schema_registryWHERE NOT COALESCE(TRY(json_schema_validate_schema(definition)), false);Quality-gate filter at ingest
Reject non-conforming payloads at the boundary; tee the rest into a quarantine table for review:
INSERT INTO events_cleanSELECT * FROM events_rawWHERE COALESCE(TRY(json_schema_validate(:event_schema, payload)), false);INSERT INTO events_quarantineSELECT * FROM events_rawWHERE NOT COALESCE(TRY(json_schema_validate(:event_schema, payload)), false);Compute a defaults patch
Given a schema with default values declared, json_schema_patch returns the JSON Patch (RFC 6902) add operations needed to fill in the missing fields:
SELECT json_schema_patch('{ "$schema": "https://json-schema.org/draft-07/schema", "type": "object", "properties": { "id": {"type": "integer", "default": 5}, "name": {"type": "string"} }}', {'name': 'George'}) AS patch;-- [{"op":"add","path":"/id","value":5}]Useful when you want to log or audit which fields were synthesized, or when the same diff has to apply to multiple downstream representations.
Apply defaults inline
When you want enriched JSON rather than the diff, reach for json_schema_update instead:
SELECT json_schema_update('{ "$schema": "https://json-schema.org/draft-07/schema", "type": "object", "properties": { "id": {"type": "integer", "default": 5}, "name": {"type": "string"} }}', {'name': 'George'}) AS updated;-- {"id":5,"name":"George"}Build a clean enriched table in one step — validate, then enrich:
CREATE OR REPLACE TABLE events_enriched ASSELECT json_schema_update(:schema, payload) AS payloadFROM events_rawWHERE COALESCE(TRY(json_schema_validate(:schema, payload)), false);Validate JSON files on ingest
Pair with DuckDB’s read_json_auto for file-based inputs:
SELECT *FROM read_json_auto('events/*.json') AS eventWHERE COALESCE(TRY(json_schema_validate(:schema, to_json(event))), false);For records arriving as already-typed JSON columns, pass the column directly without the to_json wrap.
Use a schema parameter
Keep the schema out of query text by binding it as a SQL variable — handy when the same query runs against different contracts:
SET VARIABLE event_schema = (SELECT definition FROM schema_registry WHERE name = 'events.v3');SELECT json_schema_validate_schema(getvariable('event_schema'));
SELECT COUNT(*) AS validFROM eventsWHERE COALESCE(TRY(json_schema_validate(getvariable('event_schema'), payload)), false);Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
DuckDB 2.0.0
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Apple Silicon
- Windows x86_64 x86_64 (MinGW)
- WASM Not available
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 4.18 MB |
| Linux | aarch64 | 3.53 MB |
| macOS | Apple Silicon | 2.97 MB |
| Windows | x86_64 | 10.34 MB |
| Windows | x86_64 (MinGW) | 18.01 MB |
Compressed download sizes for the release shown above.
DuckDB & Haybarn
Release calendar- DuckDB v2.0.0 Query.Farm repository Supported
- Haybarn v1.5.6 Built-in community repository Supported
Browser examples use Haybarn-Wasm 1.5.6.
Checked .