SchemaGuard
You give it data and a schema. It tells you, in one word, whether the data is safe to use.
Every piece of data coming into your system — an API request, a config file, an upload — either matches the shape you expect, or it doesn't.
SchemaGuard checks that, and hands you back exactly one of two things:
No crashes. No stack traces. No half-processed data leaking through.
What it replaces
- Annotate a class, hope the framework wires it correctly
- Reflection scans your objects at runtime
- Bad data throws an exception, sometimes deep in unrelated code
- Debugging means chasing a stack trace back to the real cause
- Write the schema once, as a plain file
- No annotations, no reflection, no hidden scanning
- Bad data never throws — you get a clear answer back
- The error tells you exactly what's wrong and where
How you actually use it
Three lines. That's the entire interaction — everything else is internal.
final var result = SchemaGuard
.checkContent(incomingData)
.basedOnSchemaInPath("user-profile")
.withAxiomParser();
Hand it the raw data you received.
Tell it which schema file to check against, by name.
Get back a clear yes/no — never a crash.
In practice: validating real JSON
Most incoming data isn't in Axiom's own format — it's plain JSON from an API request, a webhook, a queue message. SchemaGuard doesn't care. Plug in any parser you like, and it validates the result the exact same way.
final var data = SchemaGuard
.checkContent(substance)
.basedOnSchemaInPath("schemas/account_query_schema")
.withParser(toJson())
.getOrThrow();
private static Function<String, PersistentMap<String, Object>> toJson() {
return s -> JsonParser.take(s).openBuffer().ensureRootIsObject().parseObject();
}
Same call shape either way — .withAxiomParser() for Axiom's own format, .withParser(...) for anything else. The validation logic underneath doesn't change; only how the raw text gets turned into data does.
Writing a schema: the .axiom file
A schema is a plain file, not code. Write it once, drop it in schemas/, reference it by name — nothing to compile or annotate.
{
strict: true
required: [account_id, email]
properties: {
account_id: {
type: string
pattern: "^[A-Z0-9]{8,12}$"
}
email: {
type: string
pattern: "^[^@]+@[^@]+\\.[^@]+$"
}
age: {
type: integer
min: 13
max: 120
}
tags: {
minItems: 1
maxItems: 10
record: { type: string }
}
role: {
type: string
enum: [admin, member, guest]
}
}
}
| Key | What you write |
|---|---|
| type | string, integer, double, boolean — object and array can be left out, they're inferred automatically from properties / record |
| required | a list of field names that must be present |
| strict | true to reject any field not listed below |
| properties | the schema for each field of an object |
| record | the schema every item in an array must match |
| min / max | smallest and largest allowed number |
| minLength / maxLength | shortest and longest allowed string |
| pattern | a regular expression the string must match |
| minItems / maxItems | smallest and largest allowed list size |
| enum | the exact list of values allowed |
Why this matters at scale
At high transaction volume, the cost of bad data isn't a crash you notice — it's a corrupted record you don't, until it's downstream and expensive to trace back. SchemaGuard rejects malformed data at the door, with zero framework overhead, so the rest of the system only ever sees data it can trust.