Resolvers
Sometimes the default plugin behavior isn't what you need or expect. Resolvers let you patch plugins in a safe and performant way, without forking or reimplementing core logic.
Currently available for Valibot and Zod.
Examples
This page demonstrates resolvers through a few common scenarios.
Terminology
Before we look at examples, let's go through the resolvers API to help you understand how they work. Plugins that support resolvers expose them through the ~resolvers option. Each resolver is a function that receives context and returns an implemented node (or patches existing ones).
The resolver context will usually contain:
$- The node builder interface. Use it to build your custom logic.nodes- Parts of the plugin logic. You can use these to avoid reimplementing the functionality, or replace them with custom implementation.plugin- The plugin instance. You'll most likely use it to register new symbols.symbols- Frequently used symbols. These are effectively shorthands for commonly usedplugin.referenceSymbol()calls.
Other fields may include the current schema or relevant utilities.
Example 1
Handle arbitrary schema formats
By default, the Valibot plugin may produce the following schemas for date and date-time strings.
export const vDates = v.object({
created: v.pipe(v.string(), v.isoDate()),
modified: v.pipe(v.string(), v.isoTimestamp()),
});We can override this behavior by patching the nodes.format function only for strings with date or date-time formats.
{
name: 'valibot',
'~resolvers': {
string(ctx) {
const { $, schema, symbols } = ctx;
const { v } = symbols;
if (schema.format === 'date' || schema.format === 'date-time') {
ctx.nodes.format = () => $(v).attr('isoDateTime').call();
}
}
}
}This applies custom logic with surgical precision, without affecting the rest of the default behavior.
export const vDates = v.object({
created: v.pipe(v.string(), v.isoDateTime()),
modified: v.pipe(v.string(), v.isoDateTime()),
});export const vDates = v.object({
created: v.pipe(v.string(), v.isoDate()),
modified: v.pipe(v.string(), v.isoTimestamp()),
});Example 2
Validate high precision numbers
Let's say you're dealing with very large or unsafe numbers.
export const vAmount = v.number();In this case, you'll want to use a third-party library to validate your values. We can use big.js to validate all numbers by replacing the whole resolver.
{
name: 'valibot',
'~resolvers': {
number(ctx) {
const { $, plugin, symbols } = ctx;
const { v } = symbols;
const big = plugin.symbolOnce('Big', {
external: 'big.js',
importKind: 'default',
});
return $(v).attr('instance').call(big);
}
}
}We're calling plugin.symbolOnce() to ensure we always use the same symbol reference.
import Big from 'big.js';
export const vAmount = v.instance(Big);export const vAmount = v.number();Example 3
Replace default base
You might want to replace the default base schema, e.g. v.object().
export const vUser = v.object({
age: v.number(),
});Let's say we want to interpret any schema without explicitly defined additional properties as a loose object.
{
name: 'valibot',
'~resolvers': {
object(ctx) {
const { $, symbols } = ctx;
const { v } = symbols;
const additional = ctx.nodes.additionalProperties(ctx);
if (additional === undefined) {
const shape = ctx.nodes.shape(ctx);
ctx.nodes.base = () => $(v).attr('looseObject').call(shape);
}
}
}
}Above we demonstrate patching a node based on the result of another node.
export const vUser = v.looseObject({
age: v.number(),
});export const vUser = v.object({
age: v.number(),
});Feedback
We welcome feedback on the Resolvers API. Open a GitHub issue to request support for additional plugins.
Sponsors
Hey API is sponsor-funded. If your team relies on Hey API in production, consider becoming a sponsor to accelerate the roadmap.
Gold

Best-in-class developer interfaces for your API.
stainless.comThe open source coding agent.
opencode.ai
