[NestJS] Publishing a Custom Class Validator as an NPM Package
In this post, I will share the process of creating and publishing a custom class validator as an NPM package for validating user data in various projects, particularly in Korean contexts like user registration. This stems from the necessity to streamline data validation in such projects.
You can check out the open-source package on GitHub and participate in its development! :D
Introducing My NPM Package: kr-validators
Install it in your NestJS project using the following command:
1
npm i kr-validators
Step 1: Create a Project
Open Git Bash and run the following commands:
1
2
npx @nestjs/cli new kr-validators
cd kr-validators
Step 2: Create a Library
Run the commands below to set up your library:
1
2
npm i class-validator
npx nest generate library validation
This will create a libs/validation
folder, where you can write your custom decorators.
Step 3: Implementing a Decorator
Here’s how to create a decorator for validating Korean resident registration numbers:
Create a file libs/validation/src/decorators/is-resident-id-number.decorator.ts
and write the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import {
registerDecorator,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
@ValidatorConstraint({ async: false })
export class IsResidentIdNumberConstraint
implements ValidatorConstraintInterface
{
validate(value: string): boolean {
// 1. Check if the number is 13 digits long
if (!value || value.length !== 13) return false;
// 2. Multiply the first 12 digits by their weights and sum them
const weights = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5];
const digits = value.split('').map(Number);
const checkSum = weights.reduce(
(sum, weight, index) => sum + weight * digits[index],
0,
);
// 3. Calculate the check digit
const checkDigit = (11 - (checkSum % 11)) % 10;
// 4. Compare the calculated check digit with the 13th digit
return checkDigit === digits[12];
}
defaultMessage(): string {
return 'Invalid Resident ID number';
}
}
export function IsResidentIDNumber(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsResidentIdNumberConstraint,
});
};
}
- Korean resident registration numbers are 13 digits long. We first check the length.
- Multiply the first 12 digits by a predefined weight array and sum the results.
- Divide the sum by 11 and calculate the remainder, subtract it from 11, and get the remainder when divided by 10.
- If this matches the last digit of the number, the validation is successful.
Step 4: Module and Barrel Setup
- Module Setup
Edit libs/validation/src/validation.module.ts
as follows:
1
2
3
4
5
6
7
import { Module } from '@nestjs/common';
import { IsResidentIDNumber } from './decorators/is-resident-id-number.decorator';
@Module({
providers: [IsResidentIDNumber],
})
export class ValidationModule {}
- Barrel Setup
Edit libs/validation/src/index.ts
to export the necessary decorators:
1
2
export * from './decorators/is-resident-id-number.decorator';
export * from './validation.module';
Step 5: Build and Publish the Package
- Log in to NPM:
1
npm login
- Build the Library:
1
npm run build:libs
- Publish the Package:
1
npm publish
Conclusion
While this example only includes validation for resident registration numbers, I’ve also added validators for phone numbers, business numbers, card numbers, emails, and postal codes. Without these validators, developers often need to implement such checks directly in business logic for each project, which can be repetitive and inefficient.
This package addresses that need and makes validation reusable and efficient across projects. I plan to update it further, so feel free to contribute!
Thank you for reading, and happy blogging! 🚀