Schema API#

Everything typed in Janux uses one schema system, importable from janux.

Builders#

Builder JSON Schema projection Zero value
str() { type: 'string' } ''
int() { type: 'integer' } 0
num() { type: 'number' } 0
bool() { type: 'boolean' } false
money() { type: 'integer', format: 'money-minor-units' } 0
enums(['a','b']) { enum: ['a','b'] } first value
list(shapeOrType) { type: 'array', items } []
obj({...}) / schema({...}) { type: 'object', properties, required } recursive

list({ id: str() }) is shorthand for list(obj({ id: str() })).

Modifiers#

Chainable, immutable — each returns a new type:

str().nullable()          // null allowed; JSON Schema type becomes ['string','null']
int().optional()          // may be missing
int().default(1)          // applied when missing; also removes it from `required`
int().min(1).max(99)      // value bounds (length bounds for strings)

validate(type, value)#

const result = validate(cartSchema, input);
// { ok: boolean, value: unknown, errors: [{ path, message }] }
  • Missing value: .default() wins → optional passes undefinednullable passes nullrequired error.
  • Unknown object keys are stripped, never passed through.
  • Error paths are precise: items[0].qty: below min 1.

The framework calls this for you on every intent input, api input/output and event payload. You call it directly only for custom validation flows.

buildDefault(type)#

Builds initial state: explicit defaults, null for nullables, [] for lists, first enum value, zero values for primitives. This is how a component boots when no initial is provided.

toJsonSchema(type)#

Serializes to standard JSON Schema — what the manifest publishes for every tool input and resource schema, so external MCP clients and LLMs validate against the same contract the server enforces.

JxType#

The value every builder returns — the runtime type object the framework carries around. You never construct it by hand (str(), int(), obj()… do that), but you'll name it when a helper takes a schema:

import { str, type JxType } from 'janux';

function field(name: string, type: JxType) {
  return { name, json: toJsonSchema(type) };
}

field('email', str().min(3));

It's immutable: .optional(), .nullable() and .default(v) return a new JxType with the extra flag, which is why a shared base type can be reused without a modifier on one field leaking into another.

const id = str();
const maybeId = id.optional();   // `id` is unchanged

A type exposes kind ('str', 'int', 'obj', 'list'…), flags (optional / nullable / default) and, where relevant, values (enums), item (lists) or shape (objects) — the same fields toJsonSchema and urlState read.