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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "stratal-framework" with this command: npx skills add strataljs/stratal/strataljs-stratal-stratal-framework

@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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

stratal

No summary provided by upstream source.

Repository SourceNeeds Review
General

stratal-testing

No summary provided by upstream source.

Repository SourceNeeds Review
General

stratal-incremental-adoption

No summary provided by upstream source.

Repository SourceNeeds Review