Skip to content

Changelog - 2026-05-22

Schema-Qualified Casbin Tables

Breaking Change

In one line. DrizzleCasbinAdapter now targets a specific Postgres schema for its tables instead of relying on the connection's search_path.

What changed

  • Per-entity schemaName. Every entity (permission, role, policyDefinition) accepts an optional schemaName, so the three tables may live in three different schemas; each defaults to public.
  • Qualified SQL. Every FROM/JOIN now reads "<schema>"."<table>", escaped via sql.identifier, instead of a bare table name.
  • Centralized resolution. A private schemaOf() helper applies the public default at query-build time - no mutation of the caller-supplied entities object, no non-null assertions.

Who is affected

  • Casbin tables already in the public schema. No action needed - the default reproduces the previous behavior.
  • Casbin tables in a non-public schema, relying on search_path. Must act - queries will fail to resolve the relation until schemaName is set explicitly. See breaking changes below.

Breaking changes

WARNING

Only affects deployments whose Casbin tables live outside the public schema and relied on search_path to resolve them.

Before (resolved via search_path):

typescript
new DrizzleCasbinAdapter({
  dataSource,
  entities: {
    permission: { tableName: 'permissions', principalType: 'Permission' },
    role: { tableName: 'roles', principalType: 'Role' },
    policyDefinition: { tableName: 'policy_definitions', principalType: 'PolicyDefinition' },
  },
});
// Emits: FROM "policy_definitions"

After (explicit schema):

typescript
new DrizzleCasbinAdapter({
  dataSource,
  entities: {
    permission: { schemaName: 'auth', tableName: 'permissions', principalType: 'Permission' },
    role: { schemaName: 'auth', tableName: 'roles', principalType: 'Role' },
    policyDefinition: {
      schemaName: 'auth',
      tableName: 'policy_definitions',
      principalType: 'PolicyDefinition',
    },
  },
});
// Emits: FROM "auth"."policy_definitions"

Migration step: add schemaName to every entity whose table is not in public. No change needed for tables already in public.

Details

NOTE

All three entities accept schemaName, so each table can live in a different schema. The current queries only read policy_definitions and permissions in a FROM/JOIN, so role.schemaName is accepted as configuration but does not yet affect emitted SQL.

FilePackage
src/components/auth/authorize/adapters/drizzle-casbin.tscore
src/__tests__/authorize/drizzle-casbin-adapter.test.tscore