On this page
  1. 1. Install capability and binding pairs
  2. 2. Create portable services
  3. 3. Apply guards in trust order
  4. 4. Add operational boundaries explicitly
  5. Verify the boundary

Getting started with NestJS

Compose Playstack authentication, account scope, and entitlement guards into a NestJS API with explicit dependency order.

This guide builds a protected account-scoped route. It deliberately keeps the configured domain services outside NestJS so they can also be used by jobs, scripts, tests, or another application edge.

1. Install capability and binding pairs

Before you start: use an existing NestJS app with configured Auth sessions, Accounts and Entitlements. The sessions, accounts and entitlements names below refer to those application-owned instances. These are composition examples, not a complete identity backend or database migration.

New to Playstack? Run the storage quickstart first. Confirm package access; the Accounts pair requires paid access, while a first capability does not have to use Accounts.

sh
npm install \
  @playstack/auth @playstack/nest-auth \
  @playstack/accounts @playstack/nest-accounts \
  @playstack/entitlements @playstack/nest-entitlements

Keep every Playstack dependency on the same fixed version. Install only the pairs used by the first route; events, audit, queues, billing, and analytics can be composed later without changing these boundaries.

2. Create portable services

In an application composition module, configure sessions, accounts, and entitlements with application-owned persistence and infrastructure. These values are normal TypeScript services, not Nest-specific singletons.

Register their bindings at the application root:

ts
import { Module } from '@nestjs/common'
import { PlaystackAuthModule } from '@playstack/nest-auth'
import { PlaystackAccountsModule } from '@playstack/nest-accounts'
import { PlaystackEntitlementsModule } from '@playstack/nest-entitlements'
import type { AccountRequest } from '@playstack/nest-accounts'

@Module({
  imports: [
    PlaystackAuthModule.forRoot({
      sessions,
      allowedOrigins: ['https://app.example.com'],
    }),
    PlaystackAccountsModule.forRoot({ accounts }),
    PlaystackEntitlementsModule.forRoot({
      entitlements,
      resolveSubject: (request) => {
        const account = (request as AccountRequest).playstackAccount

        if (!account) {
          throw new Error('Account context must be resolved first')
        }

        return { type: 'account', id: account.account.id }
      },
    }),
  ],
})
export class ApplicationModule {}

Use each package's forRootAsync form when construction depends on other Nest providers. The resulting factory should still return explicit configured services rather than hiding environment reads throughout feature modules.

3. Apply guards in trust order

ts
import { Controller, Get, UseGuards } from '@nestjs/common'
import { PlaystackAuthGuard } from '@playstack/nest-auth'
import { PlaystackAccountGuard } from '@playstack/nest-accounts'
import { PlaystackEntitlementsGuard, RequireEntitlements } from '@playstack/nest-entitlements'

@Controller('/accounts/:accountId/packages')
export class PackagesController {
  @Get('/pro')
  @UseGuards(
    PlaystackAuthGuard,
    PlaystackAccountGuard,
    PlaystackEntitlementsGuard,
  )
  @RequireEntitlements('packages.pro')
  getProPackage(@CurrentAccount() account: AccountResolution) {
    return this.packages.forAccount(account.account.id)
  }
}

PlaystackAccountGuard revalidates membership rather than trusting the route parameter or stale session metadata. The entitlement subject resolver reads the context established by earlier guards; it does not infer an account from the URL.

For unsafe cookie-authenticated requests, run PlaystackCsrfGuard before PlaystackAuthGuard, as the cookie composition does, so a forged request is refused before any session is read; routes that set or clear session cookies add PlaystackCookieCsrfGuard, which is never bearer-exempt. Bearer-authenticated requests do not use ambient browser credentials and follow the auth adapter's fixed CSRF behavior.

The application owns the composition. Hand-place guards as above, or name a composition once and place PlaystackGuardStack wherever it belongs: guards: 'cookie' or 'bearer' selects a default per transport profile, and an ordered list mixes package guards, other bindings and your own guards, for example [PlaystackOriginGuard, TenantGuard, PlaystackAuthGuard, PlaystackAccountGuard]. globalGuards: true installs the composed stack globally, with @Public() opting routes out of the session check. No package installs a guard on its own.

4. Add operational boundaries explicitly

  • Register @playstack/nest-errors with an existing reporter and choose where its exception filter applies.
  • Register @playstack/nest-events when domain events need outbox pumping or queued observation.
  • Add @playstack/nest-rate-limit only after trusted caller and client-IP resolution are available.
  • Bind queued consumers in the worker process that owns them rather than starting every consumer inside the HTTP application.

Verify the boundary

  • No Playstack module is silently global.
  • Authentication precedes account, entitlement, role, and rate-limit checks.
  • Every account-scoped request revalidates current membership.
  • Database transactions remain application-visible.
  • Decorators declare metadata but domain services perform mutations and emit events.
  • Worker consumers and shutdown behavior are wired by the application.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software