ScopedRepository
API reference for the ScopedRepository class.
Overview
ScopedRepository is a wrapper around TypeORM's Repository that adds scope functionality. It provides methods to apply scopes and execute queries.
Creating a Scoped Repository
getScopedRepository
Create a scoped repository for an entity.
function getScopedRepository<Entity, EntityClass>(
entity: EntityClass,
dataSource: DataSource
): ScopedRepository<Entity, EntityClass>Parameters
entity- Entity classdataSource- TypeORM DataSource instance
Returns
ScopedRepository instance
Example
import { getScopedRepository } from 'typeorm-query-scopes';
import { dataSource } from './data-source';
import { User } from './entities/User';
const userRepo = getScopedRepository(User, dataSource);Methods
scope()
Apply one or more scopes to the repository.
scope(...scopeNames: (string | { method: [string, ...any[]] } | null)[]): ScopedRepositoryParameters
scopeNames- Scope names or function scope callsstring- Named scope{ method: [name, ...args] }- Function scope with parametersnullor'defaultScope'- Explicitly include default scope
Returns
New ScopedRepository instance with scopes applied (chainable)
Examples
Single Scope
const users = await userRepo.scope('verified').find();Multiple Scopes
const users = await userRepo.scope('verified', 'admin').find();Function Scope
const users = await userRepo
.scope({ method: ['byRole', 'moderator'] })
.find();Multiple Parameters
const users = await userRepo
.scope({ method: ['createdBetween', startDate, endDate] })
.find();Explicit Default Scope
const users = await userRepo
.scope('defaultScope', 'verified')
.find();Notes
- Returns a new instance (immutable)
- Scopes are applied in order
- Can be chained multiple times
unscoped()
Remove all scopes including the default scope.
unscoped(): ScopedRepositoryReturns
New ScopedRepository instance with no scopes
Example
// Get all users, including inactive
const allUsers = await userRepo.unscoped().find();Use Cases
- Admin views that need to see all records
- Maintenance operations
- Data exports
- Debugging
find()
Find entities with applied scopes.
find(options?: FindManyOptions<Entity>): Promise<Entity[]>Parameters
options- TypeORM find options (optional)
Returns
Promise resolving to array of entities
Examples
Basic
const users = await userRepo.scope('verified').find();With Additional Options
const users = await userRepo.scope('verified').find({
take: 10,
skip: 0,
order: { createdAt: 'DESC' }
});Multiple Scopes
const users = await userRepo
.scope('verified', 'withPosts')
.find({ take: 20 });findOne()
Find a single entity with applied scopes.
findOne(options: FindOneOptions<Entity>): Promise<Entity | null>Parameters
options- TypeORM find one options
Returns
Promise resolving to entity or null
Example
const user = await userRepo.scope('verified').findOne({
where: { id: 1 }
});findOneBy()
Find a single entity by conditions with applied scopes.
findOneBy(where: FindOptionsWhere<Entity>): Promise<Entity | null>Parameters
where- Where conditions
Returns
Promise resolving to entity or null
Example
const user = await userRepo.scope('verified').findOneBy({
email: 'user@example.com'
});count()
Count entities with applied scopes.
count(options?: FindManyOptions<Entity>): Promise<number>Parameters
options- TypeORM find options (optional)
Returns
Promise resolving to count
Examples
Basic
const count = await userRepo.scope('verified').count();With Conditions
const count = await userRepo.scope('verified').count({
where: { role: 'admin' }
});findAndCount()
Find entities and count total with applied scopes.
findAndCount(options?: FindManyOptions<Entity>): Promise<[Entity[], number]>Parameters
options- TypeORM find options (optional)
Returns
Promise resolving to tuple of [entities, total count]
Example
const [users, total] = await userRepo
.scope('verified')
.findAndCount({ take: 10, skip: 0 });
console.log(`Found ${users.length} users out of ${total} total`);Use Cases
- Pagination
- Displaying "X of Y results"
- Infinite scroll
- Table views with total count
Scope Merging
When multiple scopes are applied, they are merged intelligently:
WHERE Conditions
Merged using AND logic:
@Scopes<User>({
verified: { where: { isVerified: true } },
active: { where: { isActive: true } }
})
// Results in: WHERE isVerified = true AND isActive = true
await userRepo.scope('verified', 'active').find();SELECT Fields
Combined (union):
@Scopes<User>({
basicFields: { select: ['id', 'name'] },
emailField: { select: ['email'] }
})
// Results in: SELECT id, name, email
await userRepo.scope('basicFields', 'emailField').find();Relations
Combined (union):
@Scopes<User>({
withPosts: { relations: { posts: true } },
withComments: { relations: { comments: true } }
})
// Results in: loads both posts and comments
await userRepo.scope('withPosts', 'withComments').find();Order
Later scopes override earlier ones:
@Scopes<User>({
byName: { order: { name: 'ASC' } },
byDate: { order: { createdAt: 'DESC' } }
})
// Results in: ORDER BY createdAt DESC (byDate wins)
await userRepo.scope('byName', 'byDate').find();Skip/Take/Cache
Last scope wins:
@Scopes<User>({
first10: { take: 10 },
first20: { take: 20 }
})
// Results in: LIMIT 20 (first20 wins)
await userRepo.scope('first10', 'first20').find();Chaining
All methods return new instances, allowing chaining:
const repo = getScopedRepository(User, dataSource);
const users = await repo
.scope('verified')
.scope('active')
.scope({ method: ['byRole', 'admin'] })
.find({ take: 10 });Equivalent to:
const users = await repo
.scope('verified', 'active', { method: ['byRole', 'admin'] })
.find({ take: 10 });Type Safety
With type-safe scopes, TypeScript validates scope names:
@Scopes<User, {
verified: any;
admin: any;
}>({ ... })
const repo = getScopedRepository(User, dataSource);
// ✅ TypeScript knows these are valid
repo.scope('verified')
repo.scope('admin')
// ❌ TypeScript error
repo.scope('invalid')Best Practices
1. Reuse Repository Instances
class UserService {
private userRepo = getScopedRepository(User, dataSource);
async getVerified() {
return this.userRepo.scope('verified').find();
}
}2. Combine with Additional Options
// Scopes for common patterns, options for specifics
const users = await userRepo
.scope('verified', 'withPosts')
.find({
where: { country: 'US' },
take: 10
});3. Use Unscoped Carefully
// Document why unscoped is needed
async getAllUsersForExport() {
// Need all users including inactive for export
return this.userRepo.unscoped().find();
}