@stratal/framework
Higher-level framework modules for Stratal: authentication (Better Auth), database ORM (ZenStack), RBAC (Casbin), authorization guards, and test data factories. Full documentation at stratal.dev/framework.
Authentication (AuthModule)
Docs: Auth
@Module({ imports: [ AuthModule.forRootAsync({ inject: [authConfig.KEY], useFactory: (config) => ({ secret: config.secret, baseURL: config.baseURL, // ...Better Auth options }), }), ], }) export class AppModule {}
AuthContext is request-scoped and available via @inject(DI_TOKENS.AuthContext) . Key methods: isAuthenticated() , getUserId() , requireUserId() , getAuthContext() . AuthModule auto-registers session verification middleware.
Database (DatabaseModule)
Docs: Database · Database Events
Each connection brings its own ZenStack schema. Users write per-connection .zmodel files, run zenstack generate independently, and pass each connection's schema in the config.
import { PostgresDialect } from '@zenstackhq/orm/dialects/postgres'; import { Pool } from 'pg'; import { schema } from '../db/main/schema';
DatabaseModule.forRootAsync({ inject: [DI_TOKENS.CloudflareEnv], useFactory: (env: StratalEnv) => ({ default: 'main', connections: [ { name: 'main', schema, dialect: () => new PostgresDialect({ pool: new Pool({ connectionString: env.DB.connectionString, max: 1 }), }), }, ], }), })
Type augmentation:
declare module '@stratal/framework/database' { interface StratalDatabase { schemas: { main: MainSchemaType; analytics: AnalyticsSchemaType; }; defaultConnection: 'main'; } }
Inject with @inject(DI_TOKENS.Database) (default connection) or @InjectDB('name') (named). Plugins: EventEmitterPlugin , SchemaSwitcherPlugin , ErrorHandlerPlugin .
Database events follow the pattern {phase}.{Model}.{operation} — e.g., after.User.create . Augment CustomEventRegistry with DatabaseEvents<ConnectionName> for type safety.
Multi-Connection Per-Connection Schemas
Each connection has its own .zmodel file and its own zenstack generate output. For shared models, use ZenStack's native import /extends between schema files.
Per-connection schema setup:
db/ main/ schema.zmodel # Main connection models schema.ts # Generated by zenstack generate analytics/ schema.zmodel # Analytics connection models schema.ts # Generated by zenstack generate
Config with per-connection schemas:
import { schema as mainSchema } from '../db/main/schema'; import { schema as analyticsSchema } from '../db/analytics/schema';
DatabaseModule.forRootAsync({ inject: [DI_TOKENS.CloudflareEnv], useFactory: (env: StratalEnv) => ({ default: 'main', connections: [ { name: 'main', schema: mainSchema, dialect: () => ... }, { name: 'analytics', schema: analyticsSchema, dialect: () => ... }, ], }), })
Migrations — use standard ZenStack commands per connection:
zenstack db push --schema db/main/schema.zmodel zenstack db push --schema db/analytics/schema.zmodel
RBAC (RbacModule)
Docs: RBAC
RbacModule.forRoot({ model: casbinModel, defaultPolicies: [['admin', 'users:', '.']], roleHierarchy: [['super_admin', 'admin']], })
CasbinService is request-scoped. Key methods: hasPermission() , currentUserHasPermission() , hasAnyPermission() , currentUserHasAnyPermission() , addRoleForUser() , getRolesForUser() , getCurrentUserRoles() . Requires a CasbinRule model in your ZenStack schema.
AuthGuard
Docs: AuthGuard
// Auth only — checks isAuthenticated() @UseGuards(AuthGuard())
// Auth + permissions — checks isAuthenticated() + CasbinService @UseGuards(AuthGuard({ scopes: ['users:read'] }))
Throws UserNotAuthenticatedError (401) or InsufficientPermissionsError (403). Apply at class or method level.
Test Factories
Docs: Factories
export class UserFactory extends Factory<User, UserCreateInput> { protected model = 'user'; protected definition() { return { email: this.faker.internet.email(), name: this.faker.person.fullName(), }; } admin() { return this.state((attrs) => ({ ...attrs, role: 'admin' })); } }
// Usage const user = await new UserFactory().create(db); const admins = await new UserFactory().admin().count(5).createManyAndReturn(db);
Methods: make() (build without saving), create() (persist), makeMany() , createMany() , createManyAndReturn() . Use state() for variants and count() for batch creation.
Sub-path Imports
Path Key Exports
@stratal/framework/auth
AuthModule , AuthService , AuthContext
@stratal/framework/database
DatabaseModule , DatabaseService , @InjectDB , databaseI18n
@stratal/framework/rbac
RbacModule , CasbinService
@stratal/framework/guards
AuthGuard
@stratal/framework/factory
Factory , Sequence
@stratal/framework/context
RequestContext