nuxt-zenstack
v0.1.5
Published
Nuxt module for ZenStack code generation and API route wiring
Readme
nuxt-zenstack
Nuxt module for ZenStack v3.
It runs ZenStack generation during Nuxt dev/build, regenerates when your schema changes in dev, and can mount ZenStack's Nuxt API adapter.
Install
pnpm add nuxt-zenstack @zenstackhq/schema @zenstackhq/orm kysely
pnpm add -D @zenstackhq/cliFor API routes:
pnpm add @zenstackhq/serverUsage
export default defineNuxtConfig({
modules: ['nuxt-zenstack'],
zenstack: {
generate: true,
},
})Defaults:
zenstack: {
generate: {
schema: 'zenstack/schema.zmodel',
output: '.zenstack',
},
}Generated files are available through the #zenstack alias:
import { schema } from '#zenstack/schema'API Route
Enable the API route:
export default defineNuxtConfig({
modules: ['nuxt-zenstack'],
zenstack: {
api: true,
},
})api: true uses:
{
path: '/api/.zenstack/model',
configFile: 'server/zenstack.config.ts',
}ZenStack v3 requires a database dialect when creating a ZenStackClient.
The examples below use PostgreSQL with pg; install the database driver that
matches your application.
For PostgreSQL:
pnpm add pg kysely
pnpm add -D @types/pgCreate server/zenstack.config.ts and instantiate a client directly:
import { defineZenStackConfig } from 'nuxt-zenstack/server'
import { RPCApiHandler } from '@zenstackhq/server/api'
import { ZenStackClient } from '@zenstackhq/orm'
import { PostgresDialect } from 'kysely'
import { Pool } from 'pg'
import { schema } from '#zenstack/schema'
const client = new ZenStackClient(schema, {
dialect: new PostgresDialect({
pool: new Pool({
connectionString: process.env.DATABASE_URL,
}),
}),
})
export default defineZenStackConfig({
apiHandler: new RPCApiHandler({ schema }),
getClient(event) {
return client.$setAuth(/* read auth from event */)
},
})Reusing request auth in server routes
For applications that need the same client initialization and auth resolution
in the ZenStack API and other Nuxt server routes, the optional
createZenStackContext helper can manage those two lifetimes explicitly:
// server/utils/database.ts
import { createZenStackContext } from 'nuxt-zenstack/server'
import { PolicyPlugin } from '@zenstackhq/plugin-policy'
import { ZenStackClient } from '@zenstackhq/orm'
import { PostgresDialect } from 'kysely'
import { Pool } from 'pg'
import { schema } from '#zenstack/schema'
export const database = createZenStackContext({
createClient() {
return new ZenStackClient(schema, {
dialect: new PostgresDialect({
pool: new Pool({
connectionString: process.env.DATABASE_URL,
}),
}),
})
},
enhanceClient(client) {
return client.$use(new PolicyPlugin())
},
async resolve(event) {
const session = {
// Get the session from your application's auth provider.
user: { id: 'sample-user' },
}
return {
// Return undefined to use ZenStack policies anonymously.
auth: session.user,
session,
}
},
})For SQLite, replace the PostgresDialect setup with Kysely's SqliteDialect
and better-sqlite3:
pnpm add better-sqlite3
pnpm add -D @types/better-sqlite3import { SqliteDialect } from 'kysely'
import SQLite from 'better-sqlite3'
dialect: new SqliteDialect({
database: new SQLite('dev.db'),
})Use the helper's API-adapter callback in server/zenstack.config.ts:
import { defineZenStackConfig } from 'nuxt-zenstack/server'
import { RPCApiHandler } from '@zenstackhq/server/api'
import { schema } from '#zenstack/schema'
import { database } from './utils/database'
export default defineZenStackConfig({
apiHandler: new RPCApiHandler({ schema }),
getClient: database.getClient,
})The same resolved context is available in other handlers:
export default defineEventHandler(async (event) => {
const { db, session } = await database.forEvent(event)
// db is bound to the auth value returned by resolve().
})createClient and enhanceClient are each called lazily and once per module
instance. resolve and $setAuth run for every request. The helper does not
configure a database driver, auth provider, or plugins. It preserves the
concrete types returned by client extensions and also exposes
getEnhancedClient() and getUnrestrictedClient() when an application needs
direct access to either client layer. Using the helper is optional; a custom
getClient callback remains fully supported.
Options
interface ZenStackNuxtOptions {
generate?:
| boolean
| {
schema?: string
output?: string
lite?: boolean
liteOnly?: boolean
}
api?:
| boolean
| {
path?: string
configFile?: string
}
}Set generate: false if generation is handled outside Nuxt.
Development
pnpm install
pnpm dev
pnpm lint
pnpm test
pnpm test:types
pnpm prepack