Request Validator
The Validator capability checks a structured value — a parsed request body, a decoded config file, a message about to be enqueued — against a schema, and returns a full list of errors with machine-readable paths. Backends are pluggable: the same trait fronts JSON Schema, CUE, and (future) Zod / TypeBox bridges.
When To Use It
Use a ValidatorPlugin whenever you need:
- Multi-error form validation where the UI shows every problem at once, not just the first failure.
- Config-file validation at startup.
- Message shape enforcement on a queue boundary before the payload reaches a handler.
If you only need a single type check, inline serde is simpler. Reach for Validator when the schema lives outside your Rust code (JSON files, admin UI, shared with other services).
ValidatorError (plugin failure) is distinct from a ValidationReport with valid = false (successful evaluation of an invalid input). A SchemaNotFound error means you forgot to call register_schema; it is not a validation result.
The Trait
pub trait ValidatorPlugin: Send + Sync {
fn name(&self) -> &str;
fn schema_language(&self) -> &str; // "json-schema", "cue", ...
fn register_schema(&self, name: &str, source: &str) -> Result<(), ValidatorError>;
fn validate(
&self,
schema: &str,
value: &serde_json::Value,
) -> Result<ValidationReport, ValidatorError>;
}
The register-then-validate split amortises parse costs across many validation calls against the same schema. Re-registering a name replaces the previous entry.
Key Types
| Type | Purpose |
|---|---|
ValidationReport |
valid: bool plus an ordered list of errors. |
ValidationError |
path (JSON-pointer-style), message, kind. |
ValidationErrorKind |
TypeMismatch, MissingField, OutOfRange, PatternMismatch, UnknownField, Custom. |
ValidatorError |
Plugin-level failure: SchemaNotFound (400), MalformedSchema (400), Backend (500). |
ValidatorError is distinct from ValidationReport with
valid = false. The first means "the plugin couldn't do its job"; the
second means "the plugin did its job and the input was wrong".
Reference Implementations
@bext/validate-jsonschema
Minimal, dependency-free JSON Schema subset covering the keywords that cover 95% of real-world use:
type(string,number,integer,boolean,object,array,null)requiredpropertiesminimum/maximumminLength/maxLengthpatternenumitems
Perfect for HTTP form validation and config files where you want portability across tools.
@bext/validate-cue (experimental)
Experimental — not for production. A stub that demonstrates the trait
surface with a toy field: type grammar; it is not a real CUE
evaluator. Full CUE evaluation requires either a shell-out to the cue
binary or an embedded interpreter, both of which are future work. Use it
only to experiment with the trait surface.
The server selects the validator with BEXT_VALIDATOR_PROVIDER —
jsonschema (the default, recommended) or cue (experimental). The
@bext/validate-jsonschema backend is what every production deployment
should use.
Example
use bext_plugin_api::validator::*;
use serde_json::json;
// Register at startup
let plugin: &dyn ValidatorPlugin = /* @bext/validate-jsonschema */;
plugin.register_schema(
"create_user",
r#"{
"type": "object",
"required": ["email", "age"],
"properties": {
"email": { "type": "string", "pattern": "^.+@.+\\..+$" },
"age": { "type": "integer", "minimum": 13, "maximum": 150 }
}
}"#,
)?;
// Validate on each request
let body = json!({ "email": "not-an-email", "age": 12 });
let report = plugin.validate("create_user", &body)?;
if !report.valid {
for err in &report.errors {
eprintln!("{}: {} ({:?})", err.path, err.message, err.kind);
}
// returns structured errors to the caller
}
Typical output for that input:
/email: value does not match pattern (PatternMismatch)
/age: value 12 is less than minimum 13 (OutOfRange)
Note that both errors appear in one pass — the trait explicitly reports everything rather than bailing on the first mismatch.
Picking A Backend
| Scenario | Pick |
|---|---|
| Standard HTTP body validation, shared schemas across services | @bext/validate-jsonschema (recommended default). |
| Config files with simple type constraints | @bext/validate-jsonschema. |
| Experimenting with the CUE trait surface (toy grammar) | @bext/validate-cue (experimental). |
| You already have Zod / TypeBox schemas in TypeScript | A future @bext/validate-zod bridge. |
You can install multiple validator plugins side-by-side and pick one at each call site — they share no global state beyond the plugin registry.
Feature Flag
None. The Validator trait and types live in bext-plugin-api and are
always available; no cargo feature gates them.
See Also
@bext/validate-jsonschema— the minimal JSON Schema backend.@bext/validate-cue— the CUE backend slot.- Search — wire a validator before
index()to enforce document shape at the write boundary. - Webhook — validate decoded webhook payloads against a registered schema.
- Capabilities overview — the full list of pluggable capabilities.