npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

valid-fluent

v0.3.0

Published

A strongly-typed, chainable validation library for TypeScript. Craft intricate validation rules with Fluent API goodness. Go beyond boilerplate and embrace customizability.

Downloads

13

Readme

Fluent Validation Builder for TypeScript 💡

Elegant and type-safe model validation made easy.

Overview 🌐

Tired of unreadable and messy model validation logic? Meet valid-fluent Builder! Leverage the power of TypeScript and clean code principles to write clear, expressive, and type-safe validation rules for your application models.

Features 🌟

  • Fluent API design for easy readability
  • Full TypeScript support for strong type safety
  • Flexible and extensible
  • Supports conditional validation
  • Supports synchronous and asynchronous validation
  • Optional "fail fast" mode to stop validation on first error
  • In-built type-checking methods for common types (Number, String, Boolean, Date, and more)

Installation 📦

npm install valid-fluent

Basic Usage 🚀

Import the main ValidationBuilder class and start building your validation logic.

import { ValidationBuilder } from 'valid-fluent';

const validation = ValidationBuilder.create<User>()
  .forField('username', u => u.username)
  .addRule(({value}) => value !== '')
  .withMessage('Username is required')
  .build();

const result = validation.validate(user);

Type-Checking and Type-Specific Methods

Valid-Fluent now incorporates handy methods for validating common data types. Additionally, when you specify a data type using isNumber, isString, isBoolean, or isDate, you unlock type-specific validation methods for that field. Here's how to use them:


const validation = ValidationBuilder.create<User>()
    
.forField('age', u => u.age)
    .isNumber()
    .withMessage('Must be number')
    .isGreaterThan(18)
    .withMessage('You must be over 18')
.forField('email', u => u.email)
    .isString()
    .withMessage('Must be string')
    .matches(/@/)
    .withMessage('Email must contain "@"')
.forField('isActive', u => u.isActive)
    .isBoolean()
    .withMessage('Must be boolean')
    .isTrue()
    .withMessage('You must be active')
.build();

These methods not only ensure the correct data type but also provide a fluent and expressive way to define further validation rules based on the data type.

Async Validation

Asynchronous validators can be added just like regular validators. They should return a Promise.

const asyncValidation = ValidationBuilder.create<User>()
  .forField('username', u => u.username)
  .addRule(async ({value}) => {
    const userExists = await checkUserExists(value);
    return !userExists;
  })
  .withMessage('Username already exists')
  .build();

const asyncResult = await asyncValidation.validateAsync(user);

Core Concepts

The entry point for creating validations. Start by calling

ValidationBuilder.create<ModelType>()

Use .forField() method to specify the property you want to validate.

builder.forField('email', u => u.email)

Once you've added a rule, you can attach an error message using .withMessage().

builder.addRule(emailValidator)
.withMessage('Invalid email!')

You can also pass a function to withMessage to dynamically generate error messages based on the model's current state.

builder.forField('age', u => u.age)
.addRule(({ value }) => value >= 18)
.withMessage(model => `Must be at least 18 years old, but got ${model.age}.`);

In the dynamic error message example above, if the age field validation fails, the error message will reflect the actual age value from the model, providing a more descriptive error message.

Conditional Rules

Conditional rules enable dynamic validations. Use .when() to conditionally apply a validation rule.

builder.addRule(emailValidator)
.when(model => model.subscribeToNewsletter)

You can also add a condition to multiple validation rules by using the builder callback provided by when:

builder.when(model => model.subscribeToNewsletter, builder =>
  builder
      .forField('email', u => u.email)
        .addRule(emailValidator)
        .withMessage('Invalid email!')
      .forField('age', u => u.age)
        .addRule(ageValidator)
        .withMesasge('You are not old enough'));

Dependent Field Validation

To include a dependent field in validation, use the .dependsOn() method.

builder.forField('passwordConfirmation', u => u.passwordConfirmation)
.dependsOn(u => u.password)

Custom Validators

You can create custom validation rules by passing your own validator functions to .addRule().

const emailValidator = args => {
  const { value } = args;

  return value.includes('@');
};

builder.addRule(emailValidator)

You could also do this inline:

builder
    .forField('email', model => model.email)
    .addRule(args => {
      const { value } = args;
    
      return value.includes('@')
})

You can use the ValidatorArgs-type to type your validator.

Handling Validation Outcomes 🔍

Once you've defined your validation rules and run the validation, you'll receive a ValidationOutcome object. This object will be of type ValidationSuccess if the validation passes, or ValidationError if it fails. Here's how you can use this object to inspect any validation errors:

const outcome = validation.validate(user);

if (!outcome.isValid) {
    // Validation failed
    const passwordError = outcome.result.password;
    console.error(`${passwordError.propertyName}: ${passwordError.message}`);
    // Output: password: Password must match username for some reason
}

In the ValidationError object, the result field contains a ValidationResult object where each key is the name of a field in your model, and the value is an object containing the field's name and the validation error message.

Here's a more structured example using a test suite:

test('should validate password', () => {
    const outcome = validation.validate(user);
    
    if (!outcome.isValid) {
        expect(outcome.result.password.propertyName).toBe('password');
        expect(outcome.result.password.message).toBe('Password must match username for some reason');
    }
});

This structure allows you to easily access validation error messages and the corresponding field names, making error handling and reporting straightforward and type-safe.

Upcoming Features

  • [x] Async Validators
  • [ ] Validation Result Transformation
  • [ ] Custom Validation Error Handling
  • [ ] Validation Groups
  • [ ] Integration with Popular Libraries/Frameworks

Contributing 🤝

Feel free to contribute by opening issues, sending pull requests, or just by sharing your thoughts on making this package better.

License 📜

MIT License.