TypeORM extension that adds scopes and default scopes to entities and repositories, supporting both Active Record and Data Mapper patterns.
npm install typeorm-scoped> š¢ Original typeorm-scoped package for TypeORM scops.
ā
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.
``shell`
yarn add typeorm-scopedor
npm install typeorm-scoped --save
Before usage, you need to patch TypeORM before calling any database method.
`typescript
import {patchSelectQueryBuilder} from 'typeorm-scoped';
...
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.
`typescript
import {patchSelectQueryBuilder} from 'typeorm-scoped';
...
async function bootstrap() {
patchSelectQueryBuilder(); // <-- call
...
const app = await NestFactory.create(AppModule, {...});
...
`
you can define a default scope(scopes) for an entity adding the @DefaultScopes({ ... }) decorator before@Entity()
the .
`typescript
import {Entity, PrimaryGeneratedColumn, Column, BaseEntity} from "typeorm"
import {Scopes, DefaultScopes} from "typeorm-scoped"
@DefaultScopes
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;
}
`
DefaultScopes will work automatically and there shouldn't be any changes in your services or repositories. You can useEntityManager
the or any Repository or CustomRepository and the default scopes will automatically be added toQueryBuilder
the resulting query. It will also work with queries created using the . For example, the following snippet
`typescript
User.find({where: {name: "John"}});
// or
userRepository.find({where: {name: "John"}});
// or with createQueryBuilder() ...
`
will produce an SQL query like
`sql`
SELECT "User"."id" AS "User_id", "User"."name" AS "User_name"
FROM "user" "User"
WHERE "User"."name" = ? AND "User"."deletedAt" IS NULL
-- PARAMETERS: ["John"]
To define a scope(scopes) for an entity you need to add the @Scopes({ ... }) decorator before the @Entity().
`typescript
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
import {Scopes, DefaultScopes} from "typeorm-scoped";
import {ScopeEntity} from "./scope.entity";
@Scopes
females: (qb, alias) => qb.andWhere(${alias}.gender = :g, {g: "Female"}),${alias}.age >= :adultAge
adultUsers: (qb, alias, context) => qb.andWhere(, {adultAge: context.adultAge || 18}),
...
})
// 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;
}
`
If you use Active Record pattern, you need to extend from ScopeEntity:
`typescript`
@Scopes
...
})
@Entity()
export class User extends ScopeEntity { // <-- our ScopeEntity already extends from BaseEntity
...
}
If you use Data Mapper(Repository) pattern, then for custom scopes you should use Custom Repositories, which shouldextends
be from ScopeRepository:
#### Typeorm version 2.x
`typescript`
@EntityRepository(User)
export class UserRepository extends ScopeRepository
...
}
#### Typeorm version 3.x
`typescript`
// @Injectable() // <-- If you use NestJS
export class UserRepository extends ScopeRepository
constructor(private dataSource: DataSource) {
super(User, dataSource.createEntityManager());
}
}
#### For NestJS
ā __Important!__ In NestJS you will add @Injectable() on your custom repository UserRepository (forUserModule
version 3.x).
And then add it in your (if typeorm v2.x -> add into TypeOrmModule.forFeature function, if typeorm v3.x,
add into providers).
After that use it in services:
`typescript`
constructor(
private readonly userRepository: UserRepository,
...
){}
You will use custom scopes like this:
`typescript
userRepository.scoped("females").scoped("adultUsers", {adultAge: 20}).find({where: {name: "John"}});
// or
User.scoped("females").scoped("adultUsers", {adultAge: 20}).find({where: {name: "John"}});
// or with createQueryBuilder() ...
`
will produce an SQL query like
`sql`
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", 20, "John"]
You are able to disable default scopes by calling a method unscoped.
`typescript
// 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:
`typescript``
userRepository.unscoped("existed").scoped("females").find({where: {name: "John"}});
---