Scope Merging
How multiple scopes are combined when applied together.
Merging Rules
When multiple scopes are applied, they're merged using these rules:
WHERE Conditions
Combined with AND logic:
typescript
@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
Later scopes override earlier ones:
typescript
@Scopes<User>({
basic: { select: ['id', 'name'] },
detailed: { select: ['id', 'name', 'email', 'phone'] }
})
// Only uses 'detailed' select
await userRepo.scope('basic', 'detailed').find();RELATIONS
Merged together:
typescript
@Scopes<User>({
withPosts: { relations: { posts: true } },
withComments: { relations: { comments: true } }
})
// Loads both posts and comments
await userRepo.scope('withPosts', 'withComments').find();ORDER
Later scopes override earlier ones:
typescript
@Scopes<User>({
byName: { order: { name: 'ASC' } },
byDate: { order: { createdAt: 'DESC' } }
})
// Only uses byDate ordering
await userRepo.scope('byName', 'byDate').find();Default Scope Merging
Default scopes are always applied first:
typescript
@DefaultScope<User>({
where: { isActive: true }
})
@Scopes<User>({
verified: { where: { isVerified: true } }
})
// WHERE isActive = true AND isVerified = true
await userRepo.scope('verified').find();Best Practices
- Design scopes to be composable
- Avoid conflicting select/order in commonly combined scopes
- Use specific scope names to indicate merging behavior
- Test scope combinations