Skip to content

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

  1. Design scopes to be composable
  2. Avoid conflicting select/order in commonly combined scopes
  3. Use specific scope names to indicate merging behavior
  4. Test scope combinations

See Also

Released under the MIT License.