typeorm-scoped
v1.0.9
Published
TypeORM extension which adds scopes and default scopes to entities and repositories.
Downloads
517
Maintainers
Readme
typeorm-scoped
Description
✅ This lib supports Scopes for both Active Record pattern
(working with Entitiies), and Data Mapper pattern
(
working with Repositories).
It's very easy to use this lib. You can define scopes and default scopes for entities.
Installation
yarn add typeorm-scoped
# or
npm install typeorm-scoped --save
Initialization (Global Setup for Project)
Before usage you need to patch TypeORM before calling any database method.
import {patchSelectQueryBuilder} from 'typeorm-scope'
...
patchSelectQueryBuilder() // <-- call this function
...
const app = new Koa() // or const app = express() ...
In NestJS
you can call this function in main.ts
, in bootstrap()
function before creating app.
import {patchSelectQueryBuilder} from 'typeorm-scoped'
...
async function bootstrap() {
patchSelectQueryBuilder() // <-- call
...
const app = await NestFactory.create(AppModule, {...})
...
Default Scopes
you can define a default scope(scopes) for an entity adding the @DefaultScopes({ ... })
decorator before
the @Entity()
.
import {Entity, PrimaryGeneratedColumn, Column, BaseEntity} from "typeorm"
import {Scopes, DefaultScopes} from "typeorm-scoped"
@DefaultScopes<User>({
existed: (qb, alias) => qb.andWhere(`${alias}.deletedAt IS NULL`),
...
})
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column({nullable: true})
deletedAt?: Date
}
Querying
DefaultScopes
will work automatically and there shouldn't be any changes in your services or repositories. You can use
the EntityManager
or any Repository
or CustomRepository
and the default scopes
will automatically be added to
the resulting query. It will also work with queries created using the QueryBuilder
. For example, the following snippet
User.find({where: {name: "John"}})
// or
userRepository.find({where: {name: "John"}})
// or with createQueryBuilder() ...
will produce an SQL query like
SELECT "User"."id" AS "User_id", "User"."name" AS "User_name"
FROM "user" "User"
WHERE "User"."name" = ? AND "User"."deletedAt" IS NULL
-- PARAMETERS: ["John"]
Custom Scopes
To define a scope(scopes) for an entity you need to add the @Scopes({ ... })
decorator before the @Entity()
.
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"
import {Scopes, DefaultScopes} from "typeorm-scoped"
import {ScopeEntity} from "./scope.entity";
@Scopes<User>({
females: (qb, alias) => qb.andWhere(`${alias}.gender = :g`, {g: "Female"}),
adultUsers: (qb, alias) => qb.andWhere(`${alias}.age > :adultAge`, {adultAge: 17}),
...
})
// You can also use @Scopes(...) and @DefaultScopes(...) together !
@Entity()
export class User extends ScopeEntity {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column()
age: number
@Column()
gender: string // or GenderEnum -> "Male", "Female", ...
@Column({nullable: true})
deletedAt?: Date
}
Active Record Pattern
If you use Active Record
pattern, you need to extend from ScopeEntity
:
@Scopes<User>({
...
})
@Entity()
export class User extends ScopeEntity { // <-- our ScopeEntity already extends from BaseEntity
...
}
Data Mapper (Repository) Pattern
If you use Data Mapper(Repository)
pattern, then for custom scopes you should use Custom Repositories, which should
be extends
from ScopeRepository
:
Typeorm version 2.x
@EntityRepository(User)
export class UserRepository extends ScopeRepository<User> { // <-- our ScopeRepository already extends from Repository<Entity>
...
}
Typeorm version 3.x
// @Injectable() // <-- If you use NestJS
export class UserRepository extends ScopeRepository<User> {
constructor(private dataSource: DataSource) {
super(User, dataSource.createEntityManager());
}
}
For NestJS
⚠ Important! In NestJS you will add @Injectable()
on your custom repository UserRepository
(for
version 3.x).
And then add it in your UserModule
(if typeorm v2.x -> add into TypeOrmModule.forFeature function, if typeorm v3.x,
add into providers).
After that use it in services:
constructor(
...
private
readonly
userRepository: UserRepository
)
{
}
Querying
You will use custom scopes like this:
userRepository.scoped("females", "adultUsers").find({where: {name: "John"}})
// or
User.scoped("females", "adultUsers").find({where: {name: "John"}})
// or with createQueryBuilder() ...
will produce an SQL query like
SELECT "User"."id" AS "User_id", "User"."name" AS "User_name", "User"."age" AS "User_age", "User"."gender" AS "User_gender"
FROM "user" "User"
WHERE "User"."gender" = ? AND "User"."age" > ? AND "User"."name" = ?
-- PARAMETERS: ["Female", 17, "John"]
Disabling Default Scopes
You are able to disable default scopes
by calling a method unscoped
.
// if you dont send parametrs to unscoped method, it unscoped all default scopes !!!
userRepository.unscoped().find({where: {name: "John"}})
// or unscope only specific default scopes
userRepository.unscoped("existed").find({where: {name: "John"}})
// or
User.unscoped().find({where: {name: "John"}})
...
// or with createQueryBuilder() ...
...
You can also continue with scoped() method, like this:
userRepository.unscoped("existed").scoped("females").find({where: {name: "John"}})