@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/client
yarn add @nestjs/typeorm typeorm
yarn add @mikro-orm/core @mikro-orm/nestjs
yarn add @nestjs/sequelize sequelize sequelize-typescript
import { 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: [
<
8000
span class="pl-v">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
ensureExists
checks the author and returns its data if foundensureNotExists
verifies 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!