Plugins
Kanun supports installable plugins so framework-specific or optional validation behavior can live outside the core package.
This keeps the base validator small while allowing packages to add new rules, messages, type inference, output transformations, request-context helpers, and framework adapters.
Installing A Plugin
Install the core package and the plugin you need:
npm install kanun @kanun-hq/plugin-filepnpm add kanun @kanun-hq/plugin-fileyarn add kanun @kanun-hq/plugin-fileRegister the plugin once during app startup:
import { Validator } from 'kanun';
import { fileValidatorPlugin } from '@kanun-hq/plugin-file';
Validator.use(fileValidatorPlugin);Available Plugins
- File Validation: upload validation rules, file size semantics, wildcard helpers, and Express/Fastify/Hono/h3 adapters.
- Phone Validation: phone number validation, parsed
PhoneNumberoutput, formatting helpers, and Arkormˣ-style casts.
Validator Context
Plugins can read extra runtime state through validator context.
const validator = Validator.make(data, rules).withContext({
request,
currentUser,
});Context is useful when a rule depends on request-bound information such as uploaded files, authenticated users, or framework services.
If you want to register request-scoped context once in middleware and let validators created later pick it up automatically, use the static API:
import { Validator } from 'kanun';
Validator.useContext({
request,
currentUser,
});Writing Your Own Plugin
Kanun exposes a plugin API for packages that need to register rules or extend runtime behavior.
import { definePlugin } from 'kanun';
export const examplePlugin = definePlugin({
name: 'example-plugin',
install: ({ registerRule, extendTranslations, onValidationError, onValidationSuccess }) => {
registerRule('starts_with_kanun', (value) => {
return typeof value === 'string' && value.startsWith('kanun');
});
extendTranslations({
en: {
starts_with_kanun: 'The :attribute must start with kanun.',
},
});
onValidationSuccess((validator) => {
// Runs after a validator completes successfully.
});
onValidationError((validator) => {
// Runs after a validator fails and the error bag has been populated.
validator.errors().all();
});
},
});Validation Lifecycle Hooks
Plugins can register lifecycle hooks that run after each validator execution.
export const auditPlugin = definePlugin({
name: 'audit-plugin',
install: ({ onValidationSuccess, onValidationError }) => {
onValidationSuccess((validator) => {
const data = validator.validatedData();
// send successful validation metadata to your integration
});
onValidationError((validator) => {
const errors = validator.errors().all();
// send failed validation metadata to your integration
});
},
});onValidationError(...) runs after validation fails and the validator's MessageBag has been populated. onValidationSuccess(...) runs after validation passes, so plugins can safely read validatedData().
Add Rule Autocomplete For Plugin Users
If your plugin adds custom rules, you can augment Kanun's rule autocomplete so array-style rule definitions suggest your rule names in TypeScript.
declare module 'kanun' {
interface ValidationRuleAutocompleteMap {
starts_with_kanun: 'plain';
kanun_format: 'paramable';
}
}Use 'plain' for rules without parameters and 'paramable' for rules used like rule:value.
That makes plugin rules show up in array syntax such as:
const rules = {
name: ['required', 'starts_with_kanun', 'kanun_format:strict'],
};This only affects TypeScript autocomplete and typing. You still need to register the runtime rule implementation inside your plugin with registerRule(...).
If a plugin needs runtime state, document the expected shape of .withContext() and provide adapters when framework integration is common.