iso27diy-corp/AuditGlue/System alternative/JSON validation for Postgres.md

237 lines
4.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 `<SQL input>`
insert into some_table(metadata)
values
(<SQL input>);
-- <SQL input> 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/)