Skip to content

Decorator-based property validation for classes with error customization globally

License

Notifications You must be signed in to change notification settings

aritradevelops/class-validator-custom-errors

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

class-validator-custom-errors

Build Status npm version install size

This package is a fork of class-validator with the ability of customization of the error messages globally. Useful for implementing internationalization or multilingualism of the error messages or modifying the error messages in general for each validation.

Table of Contents

Installation

npm install class-validator-custom-errors --save

Note: Please use at least npm@6 when using class-validator-custom-errors. From npm@6 the dependency tree is flattened, which is required by class-validator-custom-errors to function properly.

Usage

Create your class and put some validation decorators on the properties you want to validate and add custom transformFunction to provide implementation for the errorMessage and add some custom transformKey to be passed to the transformFunction for handling the specific ones:

stackblitz playground

import {
  validate,
  validateOrReject,
  Contains,
  IsInt,
  Length,
  IsEmail,
  IsFQDN,
  IsDate,
  Min,
  Max,
} from 'class-validator-custom-errors';

export class Post {
  @Length(10, 20)
  title!: string;

  @Contains('hello', {
    transformKey: 'customContainsKey', //transformKey to be passed to handle special cases
  })
  text!: string;

  @IsInt()
  @Min(0)
  @Max(10)
  rating!: number;

  @IsEmail()
  email!: string;

  @IsFQDN()
  site!: string;

  @IsDate()
  createDate!: Date;
}

let post = new Post();
post.title = 'Hello'; // should not pass
post.text = 'this is a great post about hell world'; // should not pass
post.rating = 11; // should not pass
post.email = 'google.com'; // should not pass
post.site = 'googlecom'; // should not pass

validate(post, {
  validationError: {
    //pass a transform function to overwrite the default implementation for this validation only
    transformFunction: (key: string) => `I was called with ${key}`,
  },
}).then(errors => {
  // errors is an array of validation errors
  if (errors.length > 0) {
    console.log('validation failed. errors: ', errors);
  } else {
    console.log('validation succeed');
  }
});

validateOrReject(post, {
  validationError: {
    //pass a transform function to overwrite the default implementation for this validation only
    transformFunction: (key: string) => `I was called with ${key}`,
  },
}).catch(errors => {
  console.log('Promise rejected (validation failed). Errors: ', errors);
});
// or
async function validateOrRejectExample(input: any) {
  try {
    await validateOrReject(input, {
      validationError: {
        //pass a transform function to overwrite the default implementation for this validation only
        transformFunction: (key: string) => `I was called with ${key}`,
      },
    });
  } catch (errors) {
    console.log('Caught promise rejection (validation failed). Errors: ', errors);
  }
}

Implementing internationalization with i18next:

Full code can be found in stackblitz playground

Or clone it from here github class-validator-custom-errors-i18next

// locales/en/translation.json
{
  "success": "The request was successful!",
  "failure": "Something went wrong with the request!",
  "validation": {
    "isString": "$property must be a string.",
    "isEnum": "$property must be one of this $constraint2.",
    "isObject": "$property must be a object.",
    "uniqueString": "$property must be an unique string."
  }
}
// locales/fr/translation.json
{
  "success": "La demande a réussi!",
  "failure": "Quelque chose s'est mal passé avec la demande!",
  "validation": {
    "isString": "La $property doit être une chaîne de caractères.",
    "isEnum": "La $property doit être l'un de ceux-ci $constraint2.",
    "isObject": "La $property doit être un objet.",
    "isUnique": "La $property doit être une chaîne de caractères unique."
  }
}

// src/schemas/Url.ts
import { Type } from "class-transformer";
import { IsArray, IsEnum, IsObject, IsOptional, IsString, ValidateNested } from "class-validator-custom-errors";

export enum HttpProtocols {
  http = "http",
  https = "https",
  ws = "ws",
  wss = "wss",
  ftp = "ftp"
}

export class Query {
  @IsString()
  search!: string

  @IsArray()
  @IsOptional()
  @ValidateNested()
  select: string[] = []
}

export class Url {
  @IsString({
    transformKey: 'uniqueString'
  })
  host!: string

  @IsEnum(HttpProtocols)
  protocol!: string

  @IsString()
  tld!: string

  @IsObject()
  @ValidateNested()
  @Type(() => Query)
  query!: Query
}

// src/utils/i18nextSetup.js
import i18next from "i18next";
import I18NexFsBackend from "i18next-fs-backend";
import middleware from 'i18next-http-middleware'

i18next
  .use(I18NexFsBackend)
  .use(middleware.LanguageDetector)
  .init({
    fallbackLng: "en",
    backend: {
      loadPath: "./locales/{{lng}}/translation.json",
    },
  });

// src/main.ts
import express, { NextFunction, Request, Response } from 'express'
import i18next from 'i18next';
import middleware from 'i18next-http-middleware'
import './utils/i18nextSetup'
import 'reflect-metadata'
import { validateOrReject } from 'class-validator-custom-errors';
import { plainToClass } from 'class-transformer';
import { Url } from './schemas/Url';
const app = express()
app.use([
  middleware.handle(i18next),
  express.json(),
  express.urlencoded({ extended: true }),
]);
app.post('/urls/create', async (req: Request, res: Response, next: NextFunction) => {
  try {
    await validateOrReject(plainToClass(Url, req.body), {
      whitelist: true,
      validationError: {
        target: false,
        transformFunction: (key: string) => req.t(`validation.${key}`)
      }
    })
    res.json({ success: true, message: req.t("success"), errors: null })
  } catch (error) {
    res.json({ success: false, message: req.t("failure"), errors: error })
  }
})
export default app

// src/main.ts

import inject from 'light-my-request'
import app from './server'
(async function () {
  const responseEnglish = await inject(app, {
    method: 'POST',
    url: '/urls/create',
    headers: {
      "content-type": "application/json"
    },
    payload: JSON.stringify({ query: {} })
  })
  console.log('responseEnglish: ', JSON.stringify(responseEnglish.json(), null, 2))
  const responseFrench = await inject(app, {
    method: 'POST',
    url: '/urls/create',
    headers: {
      "content-type": "application/json",
      "accept-language": "fr"
    },
    payload: JSON.stringify({ query: {} })
  })
  console.log('responseFrench: ', JSON.stringify(responseFrench.json(), null, 2))
})()

See this for more of the basic class-validator documentation

Validation decorators

Decorator Description
Common validation decorators
@IsDefined(value: any) Checks if value is defined (!== undefined, !== null). This is the only decorator that ignores skipMissingProperties option.
@IsOptional() Checks if given value is empty (=== null, === undefined) and if so, ignores all the validators on the property.
@Equals(comparison: any) Checks if value equals ("===") comparison.
@NotEquals(comparison: any) Checks if value not equal ("!==") comparison.
@IsEmpty() Checks if given value is empty (=== '', === null, === undefined).
@IsNotEmpty() Checks if given value is not empty (!== '', !== null, !== undefined).
@IsIn(values: any[]) Checks if value is in an array of allowed values.
@IsNotIn(values: any[]) Checks if value is not in an array of disallowed values.
Type validation decorators
@IsBoolean() Checks if a value is a boolean.
@IsDate() Checks if the value is a date.
@IsString() Checks if the value is a string.
@IsNumber(options: IsNumberOptions) Checks if the value is a number.
@IsInt() Checks if the value is an integer number.
@IsArray() Checks if the value is an array
@IsEnum(entity: object) Checks if the value is a valid enum
Number validation decorators
@IsDivisibleBy(num: number) Checks if the value is a number that's divisible by another.
@IsPositive() Checks if the value is a positive number greater than zero.
@IsNegative() Checks if the value is a negative number smaller than zero.
@Min(min: number) Checks if the given number is greater than or equal to given number.
@Max(max: number) Checks if the given number is less than or equal to given number.
Date validation decorators
@MinDate(date: Date | (() => Date)) Checks if the value is a date that's after the specified date.
@MaxDate(date: Date | (() => Date)) Checks if the value is a date that's before the specified date.
String-type validation decorators
@IsBooleanString() Checks if a string is a boolean (e.g. is "true" or "false" or "1", "0").
@IsDateString() Alias for @IsISO8601().
@IsNumberString(options?: IsNumericOptions) Checks if a string is a number.
String validation decorators
@Contains(seed: string) Checks if the string contains the seed.
@NotContains(seed: string) Checks if the string not contains the seed.
@IsAlpha() Checks if the string contains only letters (a-zA-Z).
@IsAlphanumeric() Checks if the string contains only letters and numbers.
@IsDecimal(options?: IsDecimalOptions) Checks if the string is a valid decimal value. Default IsDecimalOptions are force_decimal=False, decimal_digits: '1,', locale: 'en-US'
@IsAscii() Checks if the string contains ASCII chars only.
@IsBase32() Checks if a string is base32 encoded.
@IsBase58() Checks if a string is base58 encoded.
@IsBase64(options?: IsBase64Options) Checks if a string is base64 encoded.
@IsIBAN() Checks if a string is a IBAN (International Bank Account Number).
@IsBIC() Checks if a string is a BIC (Bank Identification Code) or SWIFT code.
@IsByteLength(min: number, max?: number) Checks if the string's length (in bytes) falls in a range.
@IsCreditCard() Checks if the string is a credit card.
@IsCurrency(options?: IsCurrencyOptions) Checks if the string is a valid currency amount.
@IsISO4217CurrencyCode() Checks if the string is an ISO 4217 currency code.
@IsEthereumAddress() Checks if the string is an Ethereum address using basic regex. Does not validate address checksums.
@IsBtcAddress() Checks if the string is a valid BTC address.
@IsDataURI() Checks if the string is a data uri format.
@IsEmail(options?: IsEmailOptions) Checks if the string is an email.
@IsFQDN(options?: IsFQDNOptions) Checks if the string is a fully qualified domain name (e.g. domain.com).
@IsFullWidth() Checks if the string contains any full-width chars.
@IsHalfWidth() Checks if the string contains any half-width chars.
@IsVariableWidth() Checks if the string contains a mixture of full and half-width chars.
@IsHexColor() Checks if the string is a hexadecimal color.
@IsHSL() Checks if the string is an HSL color based on CSS Colors Level 4 specification.
@IsRgbColor(options?: IsRgbOptions) Checks if the string is a rgb or rgba color.
@IsIdentityCard(locale?: string) Checks if the string is a valid identity card code.
@IsPassportNumber(countryCode?: string) Checks if the string is a valid passport number relative to a specific country code.
@IsPostalCode(locale?: string) Checks if the string is a postal code.
@IsHexadecimal() Checks if the string is a hexadecimal number.
@IsOctal() Checks if the string is a octal number.
@IsMACAddress(options?: IsMACAddressOptions) Checks if the string is a MAC Address.
@IsIP(version?: "4"|"6") Checks if the string is an IP (version 4 or 6).
@IsPort() Checks if the string is a valid port number.
@IsISBN(version?: "10"|"13") Checks if the string is an ISBN (version 10 or 13).
@IsEAN() Checks if the string is an if the string is an EAN (European Article Number).
@IsISIN() Checks if the string is an ISIN (stock/security identifier).
@IsISO8601(options?: IsISO8601Options) Checks if the string is a valid ISO 8601 date format. Use the option strict = true for additional checks for a valid date.
@IsJSON() Checks if the string is valid JSON.
@IsJWT() Checks if the string is valid JWT.
@IsObject() Checks if the object is valid Object (null, functions, arrays will return false).
@IsNotEmptyObject() Checks if the object is not empty.
@IsLowercase() Checks if the string is lowercase.
@IsLatLong() Checks if the string is a valid latitude-longitude coordinate in the format lat, long.
@IsLatitude() Checks if the string or number is a valid latitude coordinate.
@IsLongitude() Checks if the string or number is a valid longitude coordinate.
@IsMobilePhone(locale: string) Checks if the string is a mobile phone number.
@IsISO31661Alpha2() Checks if the string is a valid ISO 3166-1 alpha-2 officially assigned country code.
@IsISO31661Alpha3() Checks if the string is a valid ISO 3166-1 alpha-3 officially assigned country code.
@IsLocale() Checks if the string is a locale.
@IsPhoneNumber(region: string) Checks if the string is a valid phone number using libphonenumber-js.
@IsMongoId() Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId.
@IsMultibyte() Checks if the string contains one or more multibyte chars.
@IsNumberString(options?: IsNumericOptions) Checks if the string is numeric.
@IsSurrogatePair() Checks if the string contains any surrogate pairs chars.
@IsTaxId() Checks if the string is a valid tax ID. Default locale is en-US.
@IsUrl(options?: IsURLOptions) Checks if the string is a URL.
@IsMagnetURI() Checks if the string is a magnet uri format.
@IsUUID(version?: UUIDVersion) Checks if the string is a UUID (version 3, 4, 5 or all ).
@IsFirebasePushId() Checks if the string is a Firebase Push ID
@IsUppercase() Checks if the string is uppercase.
@Length(min: number, max?: number) Checks if the string's length falls in a range.
@MinLength(min: number) Checks if the string's length is not less than given number.
@MaxLength(max: number) Checks if the string's length is not more than given number.
@Matches(pattern: RegExp, modifiers?: string) Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i').
@IsMilitaryTime() Checks if the string is a valid representation of military time in the format HH:MM.
@IsTimeZone() Checks if the string represents a valid IANA time-zone.
@IsHash(algorithm: string) Checks if the string is a hash The following types are supported:md4, md5, sha1, sha256, sha384, sha512, ripemd128, ripemd160, tiger128, tiger160, tiger192, crc32, crc32b.
@IsMimeType() Checks if the string matches to a valid MIME type format
@IsSemVer() Checks if the string is a Semantic Versioning Specification (SemVer).
@IsISSN(options?: IsISSNOptions) Checks if the string is a ISSN.
@IsISRC() Checks if the string is a ISRC.
@IsRFC3339() Checks if the string is a valid RFC 3339 date.
@IsStrongPassword(options?: IsStrongPasswordOptions) Checks if the string is a strong password.
Array validation decorators
@ArrayContains(values: any[]) Checks if array contains all values from the given array of values.
@ArrayNotContains(values: any[]) Checks if array does not contain any of the given values.
@ArrayNotEmpty() Checks if given array is not empty.
@ArrayMinSize(min: number) Checks if the array's length is greater than or equal to the specified number.
@ArrayMaxSize(max: number) Checks if the array's length is less or equal to the specified number.
@ArrayUnique(identifier?: (o) => any) Checks if all array's values are unique. Comparison for objects is reference-based. Optional function can be speciefied which return value will be used for the comparsion.
Object validation decorators
@IsInstance(value: any) Checks if the property is an instance of the passed value.
Other decorators
@Allow() Prevent stripping off the property when no other constraint is specified for it.

Samples

Take a look on samples in ./sample for more examples of usages.

About

Decorator-based property validation for classes with error customization globally

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 99.8%
  • JavaScript 0.2%