
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
model-validator-ts
Advanced tools
[](https://www.npmjs.com/package/model-validator-ts)
Bridging the gap between simple shape validation and business logic
A type-safe validation library for TypeScript that provides a fluent API for creating validators and commands with business rules, clear error messages and more. Built on top of the Standard Schema specification, supports zod, valibot, ArkType, etc.
Shape validation with libraries like Zod works great for basic cases. But what happens when you need to validate business rules that require external services, database lookups, how do you provide good error messages to the caller, how do you share data between rules, how easy is to test, how do you encapsulate everything together, etc.
This library is an attempt to provide a simple and opinionated way to do all of this in a simple way taking advantage of TypeScript's type system to propagate the types through the validation pipeline.
import { buildValidator } from "model-validator-ts";
import { z } from "zod";
const loginCommand = buildValidator()
// Your usual zod schema
.input(
z.object({
email: z.string().email(),
password: z.string().min(8),
})
)
.rule({
// Rules are evaluated in order, and execution stops
// if any rule adds an error to the bag
fn: async ({ data, bag }) => {
const user = await userService.findByEmail(data.email);
if (!user) {
return bag.addGlobalError("Invalid email or password");
}
if (!(await userService.validatePassword(user, data.password))) {
return bag.addGlobalError("Invalid email or password");
}
// Pass user to next rules via context
return { context: { user } };
},
})
.rule({
fn: async ({ context, bag }) => {
// Access user from previous rule
if (context.user.role === "admin") {
return bag.addError("email", "Admin users must use SSO login");
}
},
})
// Have a command (optional) that will execute the business logic
.command({
execute: async ({ context }) => {
// User is already validated and available,
return {
user: context.user,
token: await userService.generateToken(context.user),
};
},
});
// Usage
const result = await loginCommand.run({
email: "non-registered-user@example.com",
password: "securepassword",
});
if (!result.success) {
// { global: "Invalid email or password", issues: {} }
console.log(result.errors.toObject());
} else {
// If it was successful, result.result would be the command result
// { user: { id: string, email: string }, token: string }
console.log(result.result);
}
// Now use in your http handler
app.post("/login", async (req, res) => {
const result = await loginCommand.run(req.body);
if (!result.success) {
return res.status(400).json({
success: false,
errors: result.errors.toObject(),
});
}
return res.status(200).json({
success: true,
result: result.result,
});
});
// Or if using something like trpc
const loginProcedure = publicProcedure
.input(loginCommand.inputSchema)
.mutation(async ({ input }) => {
const result = await loginCommand.run(input);
if (!result.success) {
throw new trpc.TRPCError({
code: "BAD_REQUEST",
message: result.errors.toObject(),
});
}
return result.result;
});
For a more complex real-world example with multiple dependencies and rules, check out the order cancellation example or try it live in the StackBlitz playground.
npm install model-validator-ts
# or
yarn add model-validator-ts
# or
pnpm add model-validator-ts
Try out the examples directly in your browser with our StackBlitz playground - no installation needed!
If you want more complex examples, here are a few:
.input(schema)Define the input schema using any Standard Schema compatible library.
.$deps<T>()Declare the required dependencies type, forces you to provide dependencies before validation.
.rule({ fn, id?, description? })Add a business rule function. Rules can:
{ context: { key: value } }id and description for better error tracking.provide(deps)Provide the actual dependency instances. Required before validation if $deps() was called.
.validate(input, opts?)Run validation and return result with success, value/errors, and context.
.command({ execute })Create a command that combines validation with execution logic.
.provide(deps)Provide dependencies for command execution.
.run(input, opts?)Execute the command with validation + business logic.
.runShape(input, opts?)Type-safe version when input type is known.
.addError(key, message)Add an error for a specific field.
.addGlobalError(message)Add a global error not tied to a specific field.
.hasErrors()Check if any errors exist.
.firstError(key)Get the first error message for a field.
.toObject()Get errors as { field: ["error1", "error2"] }.
.flatten()Get all errors as a flat array.
.toText() / .toHtml()Format errors as text or HTML.
Validation results include detailed information about failures:
const result = await command.run(input);
if (!result.success) {
// Check which phase failed
console.log(result.step); // "validation" or "execution"
// For validation failures, see which rule failed
if (result.step === "validation" && result.rule) {
console.log(result.rule.id); // "balance-check"
console.log(result.rule.description); // "Check if account has sufficient balance"
}
// Access errors in various formats
console.log(result.errors.toObject()); // { amount: ["Insufficient funds"] }
console.log(result.errors.firstError("amount")); // "Insufficient funds"
console.log(result.errors.flatten()); // ["Insufficient funds"]
}
Check out these complete examples in the repository:
Model Validator TS: Because business logic validation shouldn't make you cry.
MIT
FAQs
[](https://www.npmjs.com/package/model-validator-ts)
We found that model-validator-ts demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.