pg_jsonschema
What it is
pg_jsonschema validates PostgreSQL json and jsonb values against JSON Schema. Validation can run in a query or become a check constraint that prevents invalid documents from being stored.
What it provides
- JSON and JSONB validation functions
- Validation of the schema document itself
- Validation diagnostics for malformed documents
Use it for flexible metadata columns, webhook payloads, configuration documents, user-defined forms, or event envelopes that need a database-enforced shape.
Use it with Rent
extension pg_jsonschema {
name = "pg_jsonschema"
}The RSL declaration makes installation and migration intent part of the reviewed application schema. Register the typed pack at runtime to call its APIs:
let jsonschema = extensions.register_pack::<PgJsonSchema>()?;
let valid_metadata = jsonschema.validates_jsonb(
post::metadata(),
serde_json::json!({"type": "object", "required": ["email"]}),
);
let posts = client
.post()
.filter_raw(valid_metadata)
.all()
.await?;
let valid = jsonschema
.client(&pool)
.validate(schema, document)
.await?;Run the application
Add the extension declaration above to your application's schema. Confirm that your PostgreSQL provider offers the extension and that your migration role can install it, then apply the migration and run your application:
rent migrate dev --name enable_pg_jsonschema
cargo runUse a database CHECK constraint when every writer must obey the JSON schema. Query-time validation only filters that query. Add NOT NULL separately if SQL NULL is forbidden.
Complete example
Download this project, or copy the files below into an empty directory.
Replace RENT_REPOSITORY_URL in Cargo.toml with the URL supplied with your preview access. Use the matching Rent CLI release.
Use an empty PostgreSQL database with the extension binaries available on its server. Set DATABASE_URL
to that database and follow any server setup requirements described above.
rent extension check --provider self-hosted
rent generate
rent migrate dev --name initial
cargo runIf your host preinstalls other extensions, declare those in RSL as well. For a database with existing application tables, baseline it first.
Cargo.toml
[package]
name = "rent-extension-pg-jsonschema"
version = "0.1.0"
edition = "2024"
[workspace]
[[bin]]
name = "rent-extension-pg-jsonschema"
path = "main.rs"
[dependencies]
rent = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6", default-features = false, features = ["postgres", "extensions", "migrate"] }
rent-ext-pg-jsonschema = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6" }
anyhow = "1.0.104"
tokio = { version = "1.53.1", features = ["full"] }
serde_json = "1.0.151"rent.toml
schema = "schema.rsl"
generated = "generated"
migrations = "migrations"
[database]
dialect = "postgres"
backends = ["postgres"]schema.rsl
datasource db {
provider = "postgresql"
}
extension pg_jsonschema {
name = "pg_jsonschema"
}
model Post {
id BigInt @id
title String
body String @default("")
}main.rs
use anyhow::Result;
use rent::{driver::DatabasePool, extension::ExtensionRegistry};
#[allow(dead_code, unused_imports)]
#[rustfmt::skip]
#[path = "generated/mod.rs"]
mod generated;
#[tokio::main]
async fn main() -> Result<()> {
let pool = DatabasePool::connect_url(&std::env::var("DATABASE_URL")?).await?;
let mut extensions = ExtensionRegistry::default();
use rent_ext_pg_jsonschema::{PgJsonSchema, PgJsonSchemaClientExt};
let pack = extensions.register_pack::<PgJsonSchema>()?;
let schema = serde_json::json!({"type":"object", "required":["email"]});
let value = serde_json::json!({"email":"maya@example.com"});
anyhow::ensure!(pack.client(&pool).validate(schema, value).await?);
println!("The metadata satisfies its schema.");
Ok(())
}