2.1.0 • Published 10 months ago

@fizbix/nest-fastest-validator v2.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
10 months ago

Nest-fastest-validator

Fastest-Validator module for Nest.JS based on the fastest-validator package.

Installation

$ npm install @fizbix/nest-fastest-validator fastest-validator class-transformer

Usage

Just import NestFastestValidatorModule to your app module:

@Module({
  imports: [NestFastestValidatorModule.forRoot()]
})
class AppModule {}

Async Options

1. UseFactory

@Module({
  imports: [
    NestFastestValidatorModule.forRootAsync({
      useFactory: () => ({
        customRules: {
          // rules
        }
      })
    })
  ]
})
class AppModule {}

2. Use class

@Injectable()
class FastestValidatorConfig implements INestFastestValidatorOptionsFactory {
  public createFastestValidatorModuleOptions(): TNestFastestValidatorModuleOptions {
    return {
      // options
    };
  }
}
@Module({
  imports: [
    NestFastestValidatorModule.forRootAsync({
      useClass: FastestValidatorConfig
    })
  ]
})
class AppModule {}

Thanks to that, every time if any unknown property is passed to the schema, an error will be thrown.

Create validation Schemas

After module configuration you can define your validation schemas:

NOTE: ValidationSchema,IsString,IsNumber, IsDate and IsShorthand are imported from the @fizbix/nest-fastest-validator package

@ValidationSchema()
export class ProductDto {
  @IsString({
    min: 3,
    max: 25
  })
  public readonly name: string;

  @IsNumber({
    integer: false,
    positive: true,
    convert: true
  })
  public readonly price: number;

  @IsDate({
    nullable: false,
    convert: true
  })
  public readonly createdAt: Date;

  @IsShorthand('string[] | optional')
  public readonly tags: string[];
}

Now - Prepare your controller:

NOTE: The NestFastestValidatorPipe is imported from @fizbix/nest-fastest-validator package

@Controller('/products')
@UsePipes(NestFastestValidatorPipe)
class ProductsController {
  @Post('/create')
  public createNewProduct(@Body() productDTO: ProductDto) {
    /// ...
  }
}

If we send request with invalid body properties - the following error will be returned to us

{
  "statusCode": 400,
  "error": "Validation failed",
  "messages": [
    {
      "field": "name",
      "message": "The 'name' field is required."
    },
    {
      "field": "price",
      "message": "The 'price' field is required."
    },
    {
      "field": "createdAt",
      "message": "The 'createdAt' field is required."
    }
  ]
}

You can configure FastestValidatorPipe by passing options to the constructor:

@UsePipes(
  new FastestValidatorPipe({
    transformToClass: true,
    disableValidationErrorMessages: true,
    httpErrorStatusCode: 404
  })
)

Globally usage:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new FastestValidatorPipe({
      disableValidationErrorMessages: true
    })
  );
  await app.listen(3000);
}
bootstrap();

Dealing with arrays

To validate arrays of objects, you can use the @IsArray decorator. Package exports function named extractSchemaRules to extract schema rules from the class. Example:

Single tag schema:

@ValidationSchema()
class Tag {
  @IsString()
  public name: string;

  @IsString()
  public description: string;

  @IsSemVer()
  public version: string;
}

And in the product schema:

@ValidationSchema()
export class Product {
  @IsNumber({
    positive: true
  })
  public id: number;

  @IsString()
  public name: string;

  @IsArray({
    items: {
      type: 'object',
      props: extractSchemaRules(Tag)
    }
  })
  public tags: Tag[];
}

Dealing with objects

To validate objects, you can use the @IsObject decorator, and pass the schema rules to the props option. Example:

@ValidationSchema()
export class Product {
  @IsNumber({
    positive: true
  })
  public id: number;

  @IsString()
  public name: string;

  @IsObject({
    props: extractSchemaRules(Tag)
  })
  public tags: Tag;
}

Validation Service

@fizbix/nest-fastest-validator package providers ValidationService class that can be used to validate data:

import { ValidationService } from '@fizbix/nest-fastest-validator';

@Controller('/products')
class ProductsController {
  constructor(private readonly validationService: ValidationService) {}
  @Post('/create')
  public createNewProduct(@Body() productDTO: ProductDto) {
    this.validator.validate(ProductDto, {
      name: 'test'
    });
    /// ...
  }
}

Validation Service has 3 methods:

  1. validateSync - returns array of validation errors or null if validation passed
  2. validate - returns Promise that resolves to validation errors or null if validation passed
  3. validateReactive - returns Observable that emits validation errors or null if validation passed

You can also use validate function from @fizbix/nest-fastest-validator package

import { validate } from '@fizbix/nest-fastest-validator';

export async function validateProductDto(dto: ProductDto) {
  const validationErrors = [];

  const validationResult = await validate(ProductDto, dto);

  if (Array.isArray(validationResult)) {
    validationErrors.push(...validationResult);
  }

  // do something with validation errors
}

Schema strict mode

Thanks to strict mode, you can control what to do when an unknown property is passed to the schema. StrictMode contains 3 options:

  1. remove - remove unknown properties from the schema
  2. throwError - throw an error when an unknown property is passed to the schema
  3. none - do nothing, and pass unknown properties to output

You can enable strict mode in 2 ways:

  1. Set strictMode: 'remove' in each schema
@ValidationSchema({
  strictMode: 'remove'
})
  1. Set globalSchemaStrictMode option in module configuration:
@Module({
  imports: [
    NestFastestValidatorModule.forRoot({
      globalSchemaStrictMode: 'throwError'
    })
  ]
})

Decorators

  1. All decorators accept an object of options that apply to the type being used, for a full list of options please refer to the fastest-validator documentation.
  2. Package contains a lot of custom decorators based on amazing class-validator package. You can use them in your schemas.

Create own custom decorators

To create a custom decorator, you can use the createCustomDecorator function from @fizbix/nest-fastest-validator package. Example:

function isJwt(value: unknown): boolean {
  return typeof value === 'string' && isJwtValidator(value);
}

const jwtRule = createCustomRule({
  dataType: 'string',
  check: ({ value, errors, path }) => {
    if (!isJwt(value)) {
      errors.push({
        type: 'JWT',
        field: path,
        expected: `Property ${path} should be a valid JWT token`,
        actual: value
      });
    }

    return value;
  }
});

export const IsJWT = (additionalOptions?: TAdditionalCustomRuleOptions) =>
  jwtRule(additionalOptions);

Type TAdditionalCustomRuleOptions is an type imported from @fizbix/nest-fastest-validator package and contains the following properties:

type TAdditionalCustomRuleOptions = {
  [x: string]: any;
  optional?: boolean | undefined;
  nullable?: boolean | undefined;
  messages?: MessagesType | undefined;
  default?: any;
};

Now you can use it on classes:

@ValidationSchema()
export class AuthenticatedUserDto {
  @IsJWT()
  public readonly token: string;
}

Async Custom Rules

Custom rules can be asynchronous. To do this, just return a Promise from the check function:

const myTestRule = createCustomRule({
  dataType: 'any',
  check: async ({ value, errors, path }) => {
    const result = await someAsyncFunction(value);

    if (!result) {
      errors.push({
        type: 'MY_TEST_RULE',
        field: path,
        expected: `Property ${path} should be a valid`,
        actual: value
      });
    }

    return value;
  }
});

const MyTestDecorator = (additionalOptions?: TAdditionalCustomRuleOptions) =>
  myTestRule(additionalOptions);

And apply the required option in @ValidationSchema decorator:

@ValidationSchema({
  isAsync: true
})
export class AuthenticatedUserDto {
  @MyTestDecorator()
  public readonly token: string;
}

Thanks to that, the check function will be called asynchronously

License

Licensed under the MIT license.

2.1.0

10 months ago

2.0.0

10 months ago

1.2.2

1 year ago

1.2.1

1 year ago

1.2.0

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago