--- tags: - json - supabase --- # JSON Schema validation for Postgres When using the JSON (or JSONB) datatype, the data needs to be validated to assure database integrity. pg_jsonschema is a PostgreSQL extension for SupaBase that can validate `json` and `jsonb` data types against a JSON Schema. The extension offers two functions: ```java -- Validates a json *instance* against a JSON Schema *schema* json_matches_schema(schema json, instance json) returns bool -- Validates a jsonb *instance* against a JSON Schema *schema* jsonb_matches_schema(schema json, instance jsonb) returns bool ``` JSON Schema is a way to define what valid JSON should look like for a particular use case: - What properties an object should have - What data types are expected - Which fields are required vs optional - Validation constraints (like minimum/maximum values, string patterns, etc.) - Default values and descriptions A JSON Schema is itself a JSON document. Here's a simple example of a JSON schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string", "description": "Person's full name" }, "age": { "type": "integer", "minimum": 0, "maximum": 150 }, "email": { "type": "string", "format": "email" } }, "required": ["name", "email"] } ``` You can check input against a schema in SQL like this: ```sql create table some_table( id serial primary key, -- db-column `id` column is an auto-incrementing primary key metadata json not null, -- db-column `metadata` must contain a JSON value and cannot be null check ( -- table-level check constraint to match the JSON in `metadata` to the schema json_matches_schema( schema :='{ "type": "object", -- we require an object ... "properties": { "foo": { -- with a single string property `"foo"` ... "type": "string" } }, "required": ["foo"], -- property `"foo"` is required ... "additionalProperties": false -- and no additional properties are allowed }', instance := metadata -- the value of the `metadata` column is passed ... -- as the `instance` argument to the `json_matches_schema` function, for each row ) ) ); -- Now we can attempt to insert a row into `some_table`, -- with the `metadata` value provided as `` insert into some_table(metadata) values (); -- needs to be replaced with an actual JSON value, e.g. '{"foo": "bar"}'. -- The insert will only succeed if the contents of `metadata` matches the schema in the check constraint. ``` ## Validating for a set of allowed values Use the `enum` keyword to validate that a value must be one of a specific set of allowed values. **String values:** ```json { "type": "string", "enum": ["red", "green", "blue"] } ``` **Mixed data types:** ```json { "enum": ["active", "inactive", null, 42] } ``` **In an object property:** ```json { "type": "object", "properties": { "status": { "type": "string", "enum": ["pending", "approved", "rejected"] }, "priority": { "type": "integer", "enum": [1, 2, 3, 4, 5] } } } ``` ## Validating for a data range **Inclusive bounds (default):** ```json { "type": "integer", "minimum": 1, "maximum": 10 } ``` This allows values from 1 to 10, including 1 and 10. **Exclusive bounds:** ```json { "type": "number", "exclusiveMinimum": 0, "exclusiveMaximum": 100 } ``` This allows values greater than 0 and less than 100, but not 0 or 100 themselves. **Mixed Bounds:** ```json { "type": "number", "minimum": 0, "exclusiveMaximum": 1 } ``` This allows values from 0 (inclusive) to 1 (exclusive), so 0 ≤ value < 1. **One-Sided Ranges:** ```json { "type": "integer", "minimum": 18 } ``` ```json { "type": "number", "maximum": 3.14159 } ``` **In Object Properties:** ```json { "type": "object", "properties": { "age": { "type": "integer", "minimum": 0, "maximum": 150 }, "temperature": { "type": "number", "minimum": -273.15, "maximum": 1000.0 } } } ``` **Regex Validation:** ```json { "type": "string", "pattern": "^[a-zA-Z0-9]+$" } ``` **Date Validation:** JSON Schema supports the ISO 8601 date format: ```json { "type": "string", "format": "date" } ``` `"date"` validates dates like: `2023-12-25` `"date-time"` validates like: `2023-12-25T10:30:00Z` or `2023-12-25T10:30:00.123Z` `"time"` validates like: `10:30:00` or `10:30:00.123` Using the ISO 8601 date format is recommended for interoperability. Custom date patterns can be validated with Regex. Ranges can be validated using the `"minimum"` and `"maximum"` keywords like before. ## Documentation** - [pg_jsonschema](https://github.com/supabase/pg_jsonschema) - [JSON Schema](https://json-schema.org/)