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

validness

v2.1.0

Published

Your favourite library for validating incoming data in express.js

Downloads

67

Readme

Validness 💦


🟢 Your favourite library for validating incoming data in express.js.

With the creation of a single class and usage of decorators, we achieve validation of the following content types (body parser required):

  • application/json
  • multipart/form-data (yes, with files and text fields)

Also, this library supports both ESM and CJS versions via dynamic exports in package.json.


⚙️ Installation:

  1. It has some important peer dependencies:
  • class-validator

  • class-transformer

  • reflect-metadata

    Optional, for multipart/form-data parsing only (These are truly optional):

  • multer (recommended)

  • formidable (yes, we support an ESM version only)

yarn add validness class-validator class-transformer reflect-metadata
npm install validness class-validator class-transformer reflect-metadata

Please import reflect-metadata in an entry file of your project in order for this package to work

import 'reflect-metadata'
// your code

For typescript decorators usage turn this option on in your tsconfig.json file.

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

✍ Usage:

Body validation

As stated above, we're going to use decorators. Let's create a DTO of commonly expected data:

registration.dto.ts

import { IsEmail, IsPhoneNumber } from "class-validator";

export class RegistrationDto {
  @IsPhoneNumber()
  phone: ValidatedFile;

  @IsEmail()
  email: string;
}

registration.controller.ts

import { Router } from "express";
import { validationBodyPipe, DefaultBodyError } from "validness";
import { RegistrationDto } from './registration.dto.ts';
import { StatusCodes } from "http-status-codes";

const router = Router()

// controller
router.post('/', validationBodyPipe(RegistrationDto), async (req, res, next) => {
  const validatedBody = req.body as RegistrationDto;

  await registerUser(validatedBody);

  res.json({ message: 'SUCCESS' })
});

// Error handling
router.use((err, req, res, next) => {
  // validness throws Default error models if customErrorFactory isn't sepcified.
  // Standard models contain everything you need, like status code and errored fields
  if (err instanceof DefaultBodyError) {
    // implement handling logic
    res.status(StatusCodes.BAD_REQUEST).json({ message: 'ERROR' });
  }
});

Query validation

Let's create a query DTO with a use of decorators. As you might know, query params always contain strings, you can transform them with class-transformer decorators.

get-users-query.dto.ts

import { IsEmail, IsNotEmpty, IsNumberString, IsPhoneNumber } from "class-validator";

export class GetUsersQueryDto {
  @IsString()
  @IsNotEmpty()
  query: string;

  @IsNumberString()
  pageIndex: string;

  @IsNumberString()
  perPage: string;
}

users.controller.ts

import { Router } from "express";
import { GetUsersQueryDto } from './get-users-query.dto.ts';
import { validationQueryPipe, DefaultQueryError } from "validness";
import { StatusCodes } from "http-status-codes";

const router = Router()

// controller
router.get('/', validationQueryPipe(GetUsersQueryDto), async (req, res, next) => {
  const validatedQuery = req.query as GetUsersQueryDto;

  await findUsers(validatedQuery);

  res.json({ message: 'SUCCESS' })
});

// Error handling
router.use((err, req, res, next) => {
  // validness throws Default error models if customErrorFactory isn't sepcified.
  // Standard models contain everything you need, like status code and errored fields
  if (err instanceof DefaultQueryError) {
    // implement handling logic
    res.status(StatusCodes.BAD_REQUEST).json({ message: 'ERROR' });
  }
});

Multipart Form Data validation

Install an underlying driver of your choice:

  • multer (highly recommended)
  • formidable (Has some nuances)
yarn add multer
yarn add -D @types/multer

npm install multer
npm install -D @types/multer

In the same simple way we create a new DTO.

sign-up.dto.ts

import { IsEmail, IsNotEmpty, IsNumberString, IsPhoneNumber, IsString } from "class-validator";
import { ValidatedFile, IsFiles, IsFile } from "validness";

export class SignUpDto {
  // form-data text field
  @IsString()
  @IsNotEmpty()
  firstName: string;

  // form-data text field
  @IsString()
  @IsNotEmpty()
  lastName: string;

  // a single file
  @IsFile({ type: 'image' })
  photo: ValidatedFile

  // a multiple files
  @IsFiles({ type: 'image' })
  documents: ValidatedFile[]
}

auth.controller.ts

import { Router } from "express";
import { SignUpDto } from './sign-up.dto.ts';
import { validationFilePipe, DefaultFileError } from "validness";
import { StatusCodes } from "http-status-codes";

const router = Router()

// controller
router.post('/', validationFilePipe(SignUpDto), async (req, res, next) => {
  const validatedBody = req.body as SignUpDto;

  // We do not use any disk storage engines for multer
  // So, validatedBody.photo.buffer is accessbile and stored in memory
  // Consult API section for more details

  await signUp(validatedBody);

  res.json({ message: 'SUCCESS' })
});

// Error handling
router.use((err, req, res, next) => {
  // validness throws Default error models if customErrorFactory isn't sepcified.
  // Standard models contain everything you need, like status code and errored fields
  if (err instanceof DefaultFileError) {
    // implement handling logic
    res.status(StatusCodes.BAD_REQUEST).json({ message: 'ERROR' });
  }
});

That's it! 😊


🤔 Nuances I have been talking about:

  1. Formidable.

IMO this library does not have a convenient API, therefore some decisions were made while developing mine.

If you decide to change core config of this library (coreConfig property), e.g. set max size for files there and not in the decorator, than the error thrown by formidable will NOT be mapped to DefaultFileError, but rather passed as is.

  1. Some options in configuration might not be complete, such as mimetype or type. If you hardly demand new ones, just open an issue and I will expend the list within a day. Same algorithm applies to any improvements you consider essential.

🛠️ Configuration

Each validation pipe has a couple of extra arguments to customise its or underlying libraries' behaviour. Let's take a look at the validationFilePipe and validationBodyPipe signatures.

// ...
export const validationFilePipe =
  (DtoConstructor: ClassConstructor, config?: ValidationFileConfig): Router => {
  }
// ...
export const validationBodyPipe =
  (DtoConstructor: ClassConstructor, config?: ValidationBodyConfig): RequestHandler => {
  }
// ...

If you want to customise config globally for all pipes use validness function and pass object with options there. (Be careful with what you change in coreConfig)

import { validness } from "validness";

validness({
  // And many other options..
  customErrorFactory: errorFactory,
  queryValidationConfig: {
    enableDebugMessages: true
  }
});

🚀 API

ValidationConfig

Defaults can be found HERE

I think an overall documentation for each property is not required, because they're well commented directly in the library itself.

If you feel a lack of examples - open an issue, I will add as many as you want. Thanks.


❤️ Contributing

If you wish to contribute in evolving of this package, please submit your issues or even open pull requests. You're always welcome. 🥰


License

MIT (c) Sole Cold