Beff is a high-performance validator generator that creates efficient runtime validators from TypeScript types using a blazing-fast compiler.
- TypeScript First: Unlike zod, io-ts, and similar libraries, Beff doesn't add overhead to the TypeScript compiler. Experience faster editor performance and quicker compile times.
- Blazingly Fast: Written in Rust and compiled to WebAssembly, Beff is cross-platform and lightning-quick. It compiles a hello-world project in 5ms and handles large projects with 200+ types in just 200ms.
- Seamlessly Compatible: Leverages the TypeScript compiler for path resolution. If your editor can resolve the types, Beff can too.
- Optimized Output: Generates highly efficient validator code with extensive compile-time optimizations.
- Developer Friendly: Provides clear, actionable error messages at both compile time and runtime.
- Feature Complete: Supports advanced TypeScript features including recursive types, generics, mapped types, conditional types, utility types (
Omit,Exclude,Partial,Required,Record), and more. If a type can be validated at runtime, Beff understands it.
Get up and running with Beff in just a few simple steps:
Install the required packages from npm:
npm i @beff/cli @beff/clientCreate a JSON file to configure Beff. The file can have any name, but it's standard practice to name it beff.json.
{
"parser": "./src/parser.ts",
"outputDir": "./src/generated"
}Create a TypeScript file that exports the types you want Beff to generate validators for.
By convention, this file is typically named parser.ts:
import parse from "./generated/parser";
type User = {
name: string;
age: number;
};
export const Parsers = parse.buildParsers<{
User: User;
}>();Run the Beff CLI to generate your validator code:
npx beff -p beff.jsonNow you can use the generated validators in your application:
import { Parsers } from "./parser.ts";
const user1 = Parsers.User.parse({
name: "John Doe",
age: 42,
});
const maybeUser = Parsers.User.safeParse(null);
const isValid: boolean = Parsers.User.validate({
name: "John Doe",
age: 42,
});
const jsonSchema = Parsers.User.schema();The beff binary can also run in watch mode.
$ npx beff -h
Usage: beff [options]
Generate validators from TypeScript types
Options:
-p, --project <string> Path to the project file
-v, --verbose Print verbose output
-w, --watch Watch for file changes
-h, --help display help for commandBeff allows you to define custom string validation formats. First, configure your beff.json:
{
"parser": "./src/parser.ts",
"outputDir": "./src/generated",
"stringFormats": [
{
"name": "ValidCurrency"
}
]
}Then use the StringFormat helper to create a branded TypeScript type and define the runtime validator:
import parse from "./generated/parser";
import { StringFormat } from "@beff/client";
export type ValidCurrency = StringFormat<"ValidCurrency">;
export const Parsers = parse.buildParsers<{
ValidCurrency: ValidCurrency;
}>({
stringFormats: {
ValidCurrency: (input: string) => {
if (VALID_CURRENCIES.include(input)) {
return true;
}
return false;
},
},
});Similarly, you can define custom number validation formats. Configure your beff.json:
{
"parser": "./src/parser.ts",
"outputDir": "./src/generated",
"numberFormats": [
{
"name": "ValidCurrency"
}
]
}Then use the NumberFormat helper to create a branded TypeScript type and define the runtime validator:
import parse from "./generated/parser";
import { NumberFormat } from "@beff/client";
export type NonNegativeNumber = NumberFormat<"NonNegativeNumber">;
export const Parsers = parse.buildParsers<{
NonNegativeNumber: NonNegativeNumber;
}>({
numberFormats: {
NonNegativeNumber: (input: number) => {
return input >= 0;
},
},
});Beff provides a runtime type creation API similar to zod and io-ts for simple use cases.
Note: This API is intentionally limited and supports only basic types to maintain fast editor performance. A more comprehensive runtime API would essentially make Beff just another re-implementation of zod, defeating its core purpose. For complex types, use Beff's primary feature of compiling TypeScript types.
Validators created with the ad-hoc API have the same interface as compiled validators:
import { b } from "@beff/client";
const AdHocItem = b.Object({
str: b.String(),
num: b.Number(),
bool: b.Boolean(),
undefined: b.Undefined(),
null: b.Null(),
any: b.Any(),
unknown: b.Unknown(),
});
const AdHocList = b.Array(AdHocItem);
const ls = AdHocList.parse([]);Beff provides seamless interoperability with Zod. Call .zod() on any Beff parser to get a compatible Zod schema.
This makes it easy to gradually migrate existing Zod-based codebases:
import { Parsers } from "./parser.ts";
import { z } from "zod";
const users = z.array(Parsers.User.zod()).parse({
name: "John Doe",
age: 42,
});Please read CONTRIBUTING.md