This package contains a neat, TypeScript-oriented solution to validating models, creating custom validation constraints that are container-aware. It leverages the awesome yup validation package.

npm i -S yup @kaviar/validator-bundle
import { a, an, Is, Schema, Nested } from "@kaviar/validator-bundle";
// a, an === yup basically
class UserRegistrationInput {
email: string;
age: number;
profile: ProfileRegistrationInput;
export class ProfileRegistrationInput {
firstName: string;
lastName: string;
const validatorService = container.get(ValidatorService);
validatorService.validate(dataSet, {
model: UserRegistrationInput,
...otherOptionsFromYup, // found in it's official documentation

Or simply validate the instance by using the class-transformer npm package:

const instance = plainToClass(UserRegistrationInput, dataSet);
// If you use the instance, it'll know the constructor and you will not have to provide the schema model
await validatorService.validate(instance);

Custom Validations#

It's always a good idea to be able to customise validations, so here is our solution:

import { Service, Inject } from "@kaviar/core";
import { yup, IValidationMethod, TestContext } from "@kaviar/validator-bundle";
export interface IUniqueFieldValidationConfig {
message?: string;
table: string;
field: string;
class UniqueFieldValidationMethod
implements IValidationMethod<IUniqueFieldValidationConfig> {
// What is your string like, which you want to validate?
parent = yup.string; // optional, defaults to yup.mixed, so to all
name = "uniqueField";
constructor() {
// Note that you can inject any dependency in the constructor, in our case, a database or api service
async validate(
value: string,
config: IUniqueFieldValidationConfig,
{ createError, path }: TestContext
) {
// The 3d argument, the context, can be found here:
const { table, field, message } = config;
let valueAlreadyExists; /* search to see if that field exists */
if (valueAlreadyExists) {
createError(message || `The field already exists`);
} else {
return "ok";

And ensure TypeScript knows about this:

// declarations.ts
import * as yup from "yup";
import { IUniqueFieldValidationConfig } from "./validator.ts";
* We need to be able to have autocompletion and extend the "yup" from within our validator.
declare module "yup" {
export class StringSchema extends yup.StringSchema {
* Specify a unique constraint for this field
uniqueField(config?: IUniqueFieldValidationConfig): StringSchema;

You now have to register the method, you add this in the prepare() phase of your bundle:

const validatorService = container.get(ValidatorService);

Now you could safely use it like this:

class UserRegistrationInput {
table: "users",
field: "email",
email: string;


Now let's say you receive from inputs a date, but not an object date, a string, "2018-12-04" you want to make it a date, so you would want to typecast it. That's done via transformers

import * as moment from "moment";
import { yup, IValidationTransformer } from "@kaviar/validator-bundle";
type IDateTransformerConfig = string;
class DateTransformer implements IValidationTransformer<IDateTransformerConfig, Date> {
// What is your string like, which you want to validate?
parent =, // optional, defaults to yup.mixed, so to all
name = "format";
// Note that this is not async
// Transformers do not support async out of the box in yup
transform(value: string, originalValue, format, schema) {
if (value instanceof Date) {
return value;
const date = moment(value, format || 'YYYY-MM-DD');
return date.isValid() ? date.toDate() : new Date();

You can add it to TypeScript declarations in the same manner as we've seen for the Validator above.

const validatorService = container.get(ValidatorService);

Now you could safely use it like this:

class PostCreateInput {
publishAt: Date;
const input = {
publishAt: "2050-12-31", // Remember this date.
const object = validatorService.validate(input, {
model: PostCreateInput,
// Casting has been doen automatically, if you want just casting: validatorService.cast(input)
object.publishAt; // instanceof Date now