@slaega/db-validation is a NestJS validation library that supports multiple ORMs (Prisma, TypeORM, MicroORM, Sequelize). It allows you to declare common database validations through a unified builder and integrate them into your services via decorators with result retrieval.
- Multi-ORM Support: Compatible with Prisma, TypeORM, MicroORM, and Sequelize
- Unified Builder: A single builder with ORM-specific methods
- Smart Validations:
exists(404 Not Found) - Returns the entity if foundensureExists(400 Bad Request) - Returns the entity if foundunique(409 Conflict)ensureNotExists(400 Bad Request)- Count validations:
ensureCountAtLeast,ensureCountAtMost,ensureCountEquals
- Decorator with Results: Retrieve validated entities directly in your methods
- Type-safe: Full TypeScript support for all ORMs
yarn add @slaega/db-validation # or npm install @slaega/db-validation- NestJS Dependencies (required in all cases)
yarn add @nestjs/common @nestjs/core reflect-metadata- ORM-specific Dependencies
yarn add @prisma/clientyarn add @nestjs/typeorm typeormyarn add @mikro-orm/core @mikro-orm/nestjsyarn add @nestjs/sequelize sequelize sequelize-typescriptimport { Module } from '@nestjs/common'; import { DbValidationModule } from '@slaega/db-validation'; import { PrismaModule } from './prisma/prisma.module'; import { PrismaService } from './prisma/prisma.service'; @Module({ imports: [ PrismaModule, DbValidationModule.registerAsync({ imports: [PrismaModule], useFactory: (prisma: PrismaService) => ({ adapter: new PrismaAdapter(prisma) }), inject: [PrismaService], }), ], }) export class AppModule {}import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DbValidationModule } from '@slaega/db-validation'; import { DataSource } from 'typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ // your TypeORM config }), DbValidationModule.forRoot({ useFactory: (dataSource: DataSource) => ({ adapter: new TypeORMAdapter(dataSource) }), inject: [DataSource] }) ] }) export class AppModule {}import { Module } from '@nestjs/common'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { DbValidationModule } from '@slaega/db-validation'; import { MikroORM } from '@mikro-orm/core'; @Module({ imports: [ MikroOrmModule.forRoot({ // your MikroORM config }), DbValidationModule.forRoot({ useFactory: (orm: MikroORM) => ({ adapter: new MikroOrmAdapter(orm) }), inject: [MikroORM] }) ] }) export class AppModule {}import { Module } from '@nestjs/common'; import { SequelizeModule } from '@nestjs/sequelize'; import { DbValidationModule } from '@slaega/db-validation'; import { Sequelize } from 'sequelize-typescript'; @Module({ imports: [ SequelizeModule.forRoot({ // your Sequelize config }), DbValidationModule.forRoot({ useFactory: (sequelize: Sequelize) => ({ adapter: new SequelizeAdapter(sequelize) }), inject: [Sequelize] }) ] }) export class AppModule {}The builder can be used directly or in a validation class. Here's the syntax for each ORM:
import { ValidationBuilder } from '@slaega/db-validation'; // Using model names as strings const builder = ValidationBuilder .forPrisma() .ensureExists('User', { id: 1 }) .unique('Post', { title: 'My title' });import { ValidationBuilder } from '@slaega/db-validation'; import { User } from './entities/user.entity'; import { Post } from './entities/post.entity'; // Using Entity classes const builder = ValidationBuilder .forTypeorm() .ensureExists(User, { id: 1 }) .unique(Post, { title: 'My title' });import { ValidationBuilder } from '@slaega/db-validation'; import { User } from './entities/user.entity'; import { Post } from './entities/post.entity'; // Using Entity classes const builder = ValidationBuilder .forMikroOrm() .ensureExists(User, { id: 1 }) .unique(Post, { title: 'My title' });import { ValidationBuilder } from '@slaega/db-validation'; import { User } from './models/user.model'; import { Post } from './models/post.model'; // Using Sequelize models const builder = ValidationBuilder .forSequelize() .ensureExists(User, { id: 1 }) .unique(Post, { title: 'My title' });For better code organization and easier mapping with the @UseDbValidation decorator, create a validation class per service. Each class contains rules for the corresponding service methods:
import { ValidationBuilder } from '@slaega/db-validation'; // With Prisma // post.validation-rules.ts export class PostValidationRules { // Rule for PostService.create create(email: string, input: CreatePostDto) { return ValidationBuilder .forPrisma() .ensureExists('Author', { email }, { message: 'Author not found', code: 'AUTHOR_NOT_FOUND' }) .ensureNotExists('Post', { title: input.title }, { message: 'Title already exists', code: 'TITLE_DUPLICATE' }); } } // 2. Using in service with result retrieval @Injectable() export class PostService { constructor( private readonly repo: PostRepository, private readonly dbValidationService: DbValidationService, // The attribute name doesn't matter ) {} // Note: The decorator automatically detects DbValidationService in the service // If you extend or modify DbValidationService, you must specify the getter: // @UseDbValidation(PostValidationRules, 'create', (self) => self.dbValidationService) // // Otherwise, automatic detection is sufficient: @UseDbValidation(PostValidationRules, 'create') async createPost( email: string, input: CreatePostDto, options?: ValidationOptions ) { // Results are in validation order const [authorResult, _] = options?.validationResult ?? []; // authorResult contains the Author object directly const author = authorResult; // You can use the validated author data return this.repo.create({ ...input, authorId: author.id, }); } }The validation flow:
- The decorator executes rules defined in
PostValidationRules.create ensureExistschecks the author and returns its data if foundensureNotExistsverifies the title doesn't exist- Results are passed to the method via
options.validationResult - You can use validated data (e.g., author) in your logic
You can also use the validator directly:
const builder = new PostValidationRules().create(userId, input); const results = await dbValidatorService.validate(builder); // Access results const [userResult] = results; console.log(userResult)| Validation | Condition | HTTP Code | Exception | OK Result |
|---|---|---|---|---|
| exists | Record found | 404 | NotFoundException | ✅ Found object |
| ensureExists | Record found | 400 | BadRequestException | ✅ Found object |
| unique | No duplicate | 409 | ConflictException | ✅ true |
| ensureNotExists | No duplicate | 400 | BadRequestException | ✅ true |
| ensureCountAtLeast | Count ≥ min | 400 | BadRequestException | ✅ { count: number } |
| ensureCountAtMost | Count ≤ max | 400 | BadRequestException | ✅ { count: number } |
| ensureCountEquals | Count = val | 400 | BadRequestException | ✅ { count: number } |
// For exists/ensureExists const [userResult] = await service.validate(builder); console.log(userResult); // { id: 1, email: 'user@example.com', ... } // For unique/ensureNotExists const [uniqueResult] = await service.validate(builder); console.log(uniqueResult); // true // For count validations const [countResult] = await service.validate(builder); console.log(countResult); // { count: 5 }-
Clone & install
git clone https://github.com/slaega/db-validation.git cd db-validation yarn install -
Build
yarn build
-
Link in a project
yarn link cd ../your-app yarn link @slaega/db-validation yarn install -
Tests
yarn test yarn test:watch
- Fork the repo
- Create a branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'Add feature') - Push to your branch (
git push origin feature/my-feature) - Open a Pull Request
This project is under the MIT license. See LICENSE for more details.
Maintained by Slaega. Feel free to open issues on GitHub!