### Initialize and Run ZenStack Project Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/next.js/README.md Commands to install dependencies, initialize the database, and start the development server. ```bash pnpm install pnpm db:init pnpm dev ``` -------------------------------- ### Install @zenstackhq/server Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/server/README.md Install the @zenstackhq/server package using npm. ```bash npm install @zenstackhq/server ``` -------------------------------- ### Install ZenStack Better-Auth Adapter Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/auth-adapters/better-auth/README.md Install the required package using npm. ```bash npm install @zenstackhq/better-auth@next ``` -------------------------------- ### Install @zenstackhq/schema Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/schema/README.md Install the ZenStack schema package using npm. ```bash npm install @zenstackhq/schema ``` -------------------------------- ### Start Svelte development server Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/sveltekit/README.md Run 'npm run dev' to start the development server. Use '-- --open' to automatically open the application in a new browser tab. ```sh npm run dev # or start the server and open the app in a new browser tab npm run dev -- --open ``` -------------------------------- ### Install and build project dependencies Source: https://github.com/zenstackhq/zenstack/blob/dev/CONTRIBUTING.md Standard commands for setting up the development environment and building the monorepo packages. ```bash pnpm install ``` ```bash pnpm build ``` ```bash pnpm test ``` -------------------------------- ### Start Development Server Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/nuxt/README.md Starts the Nuxt development server for local testing and development. The application will be accessible at http://localhost:3302. ```bash pnpm dev ``` -------------------------------- ### Install @zenstackhq/orm Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/orm/README.md Install the core ZenStack ORM engine using npm. ```bash npm install @zenstackhq/orm ``` -------------------------------- ### Install Dependencies with PNPM Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/nuxt/README.md Installs project dependencies using the PNPM package manager. Ensure PNPM is installed globally. ```bash pnpm install ``` -------------------------------- ### Install ZenStack dependencies manually Source: https://github.com/zenstackhq/zenstack/blob/dev/README.md Install the CLI and ORM packages manually if not using the automated initialization. ```bash npm install -D @zenstackhq/cli npm install @zenstackhq/orm ``` -------------------------------- ### Install ZenStack CLI Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/cli/README.md Install the ZenStack CLI as a development dependency in your project using npm. ```bash npm install -D @zenstackhq/cli ``` -------------------------------- ### REST API: Get single user by ID Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of retrieving a single user by their ID using curl. ```bash # Get single user by ID curl -X GET "http://localhost:3000/api/model/user/clx123abc" ``` -------------------------------- ### Create ZenStack Client with SQLite Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/orm/README.md Instantiates a ZenStack client for interacting with a SQLite database. Requires schema definition and Kysely dialect setup. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { schema } from './zenstack/schema'; import SQLite from 'better-sqlite3'; import { SqliteDialect } from 'kysely'; const db = ZenStackClient(schema, { dialect: new SqliteDialect({ database: new SQLite('./zenstack/dev.db') }), }); ``` -------------------------------- ### REST API: Create a resource Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of creating a new user resource with attributes using curl and JSON payload. ```bash # Create a resource curl -X POST "http://localhost:3000/api/model/user" \ -H "Content-Type: application/json" \ -d '{ "data": { "type": "user", "attributes": { "email": "new@example.com", "name": "New User" } } }' ``` -------------------------------- ### REST API: List all users with pagination Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of listing all users with pagination parameters using curl. ```bash # List all users with pagination curl -X GET "http://localhost:3000/api/model/user?page[limit]=10&page[offset]=0" ``` -------------------------------- ### Install @zenstackhq/zod Package Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/zod/README.md Install the Zod integration package for ZenStack using npm. This package provides Zod schemas for validating data based on your ZModel definitions. ```bash npm install @zenstackhq/zod ``` -------------------------------- ### REST API: Include relations Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of including related resources (e.g., posts and profile) when fetching users using curl. ```bash # Include relations curl -X GET "http://localhost:3000/api/model/user?include=posts,profile" ``` -------------------------------- ### REST API: Call custom procedure (mutation) Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of calling a custom server-side procedure (mutation) like 'signUp' using curl with a JSON payload. ```bash # Call custom procedure (mutation) curl -X POST "http://localhost:3000/api/model/$procs/signUp" \ -H "Content-Type: application/json" \ -d '{ "email": "proc@example.com", "name": "Proc User" }' ``` -------------------------------- ### REST API: Select specific fields Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of selecting specific fields (id, name, email) for user resources using curl. ```bash # Select specific fields curl -X GET "http://localhost:3000/api/model/user?fields[user]=id,name,email" ``` -------------------------------- ### REST API: Sort results Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of sorting posts by creation date (descending) and title (ascending) using curl. ```bash # Sort results curl -X GET "http://localhost:3000/api/model/post?sort=-createdAt,title" ``` -------------------------------- ### REST API: Call custom procedure (query) Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of calling a custom server-side procedure (query) like 'listPublicPosts' using curl. ```bash # Call custom procedure (query) curl -X GET "http://localhost:3000/api/model/$procs/listPublicPosts" ``` -------------------------------- ### REST API: Delete a resource Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of deleting a user resource by ID using curl. ```bash # Delete a resource curl -X DELETE "http://localhost:3000/api/model/user/clx123abc" ``` -------------------------------- ### REST API: Update a resource Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of updating an existing user resource by ID using curl and a JSON PATCH payload. ```bash # Update a resource curl -X PATCH "http://localhost:3000/api/model/user/clx123abc" \ -H "Content-Type: application/json" \ -d '{ "data": { "type": "user", "attributes": { "name": "Updated Name" } } }' ``` -------------------------------- ### REST API: Filter users by field Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of filtering users by a specific field value using curl. ```bash # Filter users by field curl -X GET "http://localhost:3000/api/model/user?filter[role]=ADMIN" ``` -------------------------------- ### ZModel Schema Definition Source: https://context7.com/zenstackhq/zenstack/llms.txt Define data models, enums, plugins, and access policies using ZModel syntax. This example includes reusable type definitions, an enum, a policy plugin, and models for User, Profile, and Post with computed fields and relations. ```zmodel // zenstack/schema.zmodel datasource db { provider = 'sqlite' url = 'file:./dev.db' } // Reusable type definitions type CommonFields { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum Role { ADMIN USER } // Enable policy plugin for access control plugin policy { provider = '@zenstackhq/plugin-policy' } model User with CommonFields { email String @unique name String? role Role @default(USER) postCount Int @computed // Computed field posts Post[] profile Profile? // Access policies @@allow('read,create', true) @@allow('all', this == auth()) } model Profile with CommonFields { bio String? age Int? user User? @relation(fields: [userId], references: [id]) userId String? @unique @@allow('all', check(user)) } model Post with CommonFields { title String content String published Boolean @default(false) author User @relation(fields: [authorId], references: [id]) authorId String @@allow('read', published) @@allow('all', author == auth()) } // Custom procedures mutation procedure signUp(email: String, name: String?, role: Role?): User procedure listPublicPosts(): Post[] ``` -------------------------------- ### REST API: Filter with operators Source: https://context7.com/zenstackhq/zenstack/llms.txt Example of filtering posts where the title contains a specific string using curl. Supports operators like $lt, $lte, $gt, $gte, $contains, $startsWith, $endsWith, $between. ```bash # Filter with operators curl -X GET "http://localhost:3000/api/model/post?filter[title$contains]=TypeScript" # Operators: $lt, $lte, $gt, $gte, $contains, $startsWith, $endsWith, $between ``` -------------------------------- ### Count Records with Filters Source: https://context7.com/zenstackhq/zenstack/llms.txt Use the `count` method to get the total number of records or count records that match specific filter criteria. The `select` option can be used to count records based on a specific field. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Count records const totalUsers = await db.user.count(); // Returns: 42 // Count with filter const adminCount = await db.user.count({ where: { role: 'ADMIN' }, }); // Count by field const countByRole = await db.user.count({ select: { role: true }, }); // Returns: { role: 5 } (count of records with non-null role) ``` -------------------------------- ### Initialize ZenStack Project Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Use the ZenStack CLI to initialize a new project. Ensure npx is available. ```bash npx zenstack init ``` -------------------------------- ### Initialize ZenStack in an existing project Source: https://github.com/zenstackhq/zenstack/blob/dev/README.md Use the CLI to add ZenStack configuration to an existing project. ```bash npx @zenstackhq/cli@latest init ``` -------------------------------- ### Build and Watch All Packages Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Use these commands to build or watch all packages in the monorepo. Requires pnpm and Turbo. ```bash pnpm build ``` ```bash pnpm watch ``` -------------------------------- ### Initialize Database and Seed Data Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/nuxt/README.md Initializes the database and populates it with seed data using a provided script. This is a crucial step before running the application. ```bash pnpm db:init ``` -------------------------------- ### Initialize ZenStack Client and Query User Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/orm/README.md Initialize the ZenStack client with your schema and dialect configuration, then perform a findFirst query to retrieve a user by email. Ensure your schema file is correctly imported. ```typescript import { ZenStackClient } from '@zenstack/orm'; import schema from './schema'; const client = new ZenStackClient(schema, { /* dialect config */ }); const user = await client.user.findFirst({ where: { email: 'alice@example.com' } }); ``` -------------------------------- ### Create New ZenStack Project Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/create-zenstack/README.md Use this command to scaffold a new ZenStack project with the latest version of the create-zenstack tool. Specify 'my-app' as the project directory name. ```bash npm create zenstack@latest my-app ``` -------------------------------- ### Create a new Svelte project Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/sveltekit/README.md Use 'npx sv create' to initialize a new Svelte project. Specify a directory name to create the project in a subfolder. ```sh npx sv create # create a new project in my-app npx sv create my-app ``` -------------------------------- ### Configure Better-Auth with ZenStack Adapter Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/auth-adapters/better-auth/README.md Initialize the Better-Auth instance using the zenstackAdapter with your database client. ```ts import { zenstackAdapter } from '@zenstackhq/better-auth'; // ZenStack ORM client import { db } from './db'; const auth = new BetterAuth({ database: zenstackAdapter(db, { provider: 'postgresql', // or 'sqlite' }), // other better-auth options... }); ``` -------------------------------- ### Lint and Test All Packages Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Run ESLint for code quality checks and execute all package tests. Requires pnpm. ```bash pnpm lint ``` ```bash pnpm test ``` -------------------------------- ### Manage Database Migrations Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Create and apply database migrations using the ZenStack CLI. Prisma is used under the hood for migration management. ```bash npx zenstack migrate dev ``` ```bash npx zenstack migrate deploy ``` -------------------------------- ### ZenStack CLI Commands Source: https://context7.com/zenstackhq/zenstack/llms.txt A collection of essential ZenStack CLI commands for project initialization, code generation, schema checking, formatting, database migrations, and introspection. ```bash # Initialize ZenStack in an existing project npx @zenstackhq/cli@latest init ``` ```bash # Generate TypeScript code from ZModel schema npx zenstack generate # Options: --watch, --output , --lite, --generate-models, --generate-input ``` ```bash # Check schema for errors npx zenstack check --schema ./zenstack/schema.zmodel ``` ```bash # Format schema file npx zenstack format --schema ./zenstack/schema.zmodel ``` ```bash # Database migrations (uses Prisma under the hood) npx zenstack migrate dev --name "add_user_table" npx zenstack migrate deploy npx zenstack migrate status npx zenstack migrate reset --force ``` ```bash # Push schema to database (development) npx zenstack db push --accept-data-loss ``` ```bash # Introspect database to generate schema npx zenstack db pull --output ./zenstack/schema.zmodel ``` ```bash # Seed database (configure in package.json under "zenstack.seed") npx zenstack db seed ``` ```bash # Start proxy server for development npx zenstack proxy --port 2311 ``` ```bash # Get info about installed packages npx zenstack info ``` -------------------------------- ### Initialize ZenStackClient with Dialects Source: https://context7.com/zenstackhq/zenstack/llms.txt Configure ZenStackClient with specific database dialects, optional computed fields, custom procedures, and logging. Supports SQLite, PostgreSQL, and MySQL backends. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { SqliteDialect } from '@zenstackhq/orm/dialects/sqlite'; import { PostgresDialect } from '@zenstackhq/orm/dialects/postgres'; import { MysqlDialect } from '@zenstackhq/orm/dialects/mysql'; import SQLite from 'better-sqlite3'; import { Pool } from 'pg'; import { createPool } from 'mysql2'; import { sql } from 'kysely'; import { schema } from './zenstack/schema'; // SQLite configuration const sqliteDb = new ZenStackClient(schema, { dialect: new SqliteDialect({ database: new SQLite('./zenstack/dev.db'), }), // Optional: computed fields implementation computedFields: { user: { postCount: (eb, { modelAlias }) => eb .selectFrom('Post') .whereRef('Post.authorId', '=', sql.ref(`${modelAlias}.id`)) .select(({ fn }) => fn.countAll().as('postCount')), }, }, // Optional: custom procedures implementation procedures: { signUp: ({ client, args }) => client.user.create({ data: { ...args } }), listPublicPosts: ({ client }) => client.post.findMany({ where: { published: true }, orderBy: { updatedAt: 'desc' }, }), }, // Optional: logging configuration log: ['query', 'error'], // Optional: input validation (default: true) validateInput: true, }); // PostgreSQL configuration const pgDb = new ZenStackClient(schema, { dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }), }), }); // MySQL configuration const mysqlDb = new ZenStackClient(schema, { dialect: new MysqlDialect({ pool: createPool({ uri: process.env.DATABASE_URL }), }), }); // Connect and disconnect await sqliteDb.$connect(); await sqliteDb.$disconnect(); ``` -------------------------------- ### Scaffold a new ZenStack project Source: https://github.com/zenstackhq/zenstack/blob/dev/README.md Use this command to create a new TypeScript project with ZenStack pre-configured. ```bash npm create zenstack@latest my-project ``` -------------------------------- ### Configure Postgres connection string Source: https://github.com/zenstackhq/zenstack/blob/dev/CONTRIBUTING.md The default connection string format used for running tests against a Postgres database. ```text postgresql://${TEST_PG_USER}:${TEST_PG_PASSWORD}$@${TEST_PG_HOST}$:${TEST_PG_PORT} ``` -------------------------------- ### Build Svelte project for production Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/sveltekit/README.md Execute 'npm run build' to generate a production-ready build of your Svelte application. Preview the build with 'npm run preview'. ```sh npm run build ``` -------------------------------- ### Extend ZenStackClient with Plugins Source: https://context7.com/zenstackhq/zenstack/llms.txt Use runtime plugins to intercept queries, log operations, enforce policies, and compute extended fields. Plugins can be chained, removed by ID, or cleared entirely. ```typescript import { ZenStackClient, type RuntimePlugin } from '@zenstackhq/orm'; import { PolicyPlugin } from '@zenstackhq/plugin-policy'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Create a logging plugin const loggingPlugin: RuntimePlugin = { id: 'logging', name: 'Query Logger', description: 'Logs all queries with timing', onQuery: async ({ model, operation, args, proceed }) => { const start = Date.now(); console.log(`[Query] ${model}.${operation}`, JSON.stringify(args)); const result = await proceed(args); console.log(`[Query] ${model}.${operation} completed in ${Date.now() - start}ms`); return result; }, }; // Apply plugin with $use const dbWithLogging = db.$use(loggingPlugin); // Chain multiple plugins const dbWithPlugins = db .$use(loggingPlugin) .$use(new PolicyPlugin()); // Remove a plugin by ID const dbWithoutLogging = dbWithPlugins.$unuse('logging'); // Remove all plugins const plainDb = dbWithPlugins.$unuseAll(); // Access Policy Plugin for authorization const policyDb = db.$use(new PolicyPlugin()); // Set authenticated user context const userDb = policyDb.$setAuth({ id: 'user-id', role: 'USER' }); // Queries now respect @@allow policies from schema const accessiblePosts = await userDb.post.findMany(); // Only returns posts the user is allowed to read // Get current auth context const auth = userDb.$auth; // Returns: { id: 'user-id', role: 'USER' } // Extended result fields via plugins const extResultPlugin: RuntimePlugin = { id: 'ext-result', result: { user: { fullName: { needs: { name: true }, compute: (data) => data.name?.toUpperCase() ?? 'ANONYMOUS', }, }, }, }; const dbWithExtResult = db.$use(extResultPlugin); const usersWithFullName = await dbWithExtResult.user.findMany({ select: { id: true, fullName: true }, }); ``` -------------------------------- ### Generate Better-Auth Schema Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/auth-adapters/better-auth/README.md Use the CLI to populate better-auth data models into your ZModel schema. ```bash npx @better-auth/cli generate ``` -------------------------------- ### Define and Execute Custom Procedures Source: https://context7.com/zenstackhq/zenstack/llms.txt Encapsulate business logic by defining mutation and query procedures within the ZenStackClient configuration. Procedures are invoked via the $procs interface and respect applied plugins. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { schema } from './zenstack/schema'; // Define procedures in ZModel: // mutation procedure signUp(email: String, name: String?): User // procedure listPublicPosts(): Post[] const db = new ZenStackClient(schema, { dialect: /* ... */, procedures: { // Mutation procedure (modifies data) signUp: async ({ client, args }) => { const { email, name } = args; // Validate email format if (!email.includes('@')) { throw new Error('Invalid email format'); } return client.user.create({ data: { email, name }, }); }, // Query procedure (read-only) listPublicPosts: async ({ client }) => { return client.post.findMany({ where: { published: true }, orderBy: { createdAt: 'desc' }, include: { author: { select: { name: true } } }, }); }, }, }); // Call procedures via $procs const newUser = await db.$procs.signUp({ args: { email: 'new@example.com', name: 'New User' }, }); const publicPosts = await db.$procs.listPublicPosts(); // Procedures respect plugins (including policy enforcement) const policyDb = db.$use(new PolicyPlugin()).$setAuth({ id: 'admin-id' }); const protectedProcResult = await policyDb.$procs.signUp({ args: { email: 'protected@example.com' }, }); ``` -------------------------------- ### Enable auto formatting on save Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/ide/vscode/README.md Configure VS Code to automatically format your files upon saving by adding this to your .vscode/settings.json. ```json { "editor.formatOnSave": true } ``` -------------------------------- ### Sync Schema to Database Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Synchronize your ZModel schema with the database. This command utilizes Prisma for database operations. ```bash npx zenstack db push ``` -------------------------------- ### Access Raw Kysely Instance Source: https://context7.com/zenstackhq/zenstack/llms.txt Obtain the raw Kysely query builder instance via `$qbRaw` to perform operations directly without the ZenStack ORM layer, useful for advanced or low-level database interactions. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { sql } from 'kysely'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Access raw Kysely without ZenStack query executor const rawKysely = db.$qbRaw; await rawKysely .insertInto('User') .values({ id: 'custom-id', email: 'raw@example.com' }) .execute(); ``` -------------------------------- ### Raw SQL Queries Source: https://context7.com/zenstackhq/zenstack/llms.txt Execute raw SQL queries using `$queryRaw` with tagged template literals for type safety or `$queryRawUnsafe` for direct execution with parameters. Use `$executeRaw` for mutations. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { sql } from 'kysely'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Raw queries with tagged template literals const rawUsers = await db.$queryRaw<{ id: string; email: string }[]>`` SELECT id, email FROM "User" WHERE role = 'ADMIN' `; // Raw query with parameters (unsafe - use with caution) const rawUsersUnsafe = await db.$queryRawUnsafe<{ id: string }[]> 'SELECT id FROM "User" WHERE email = $1', 'john@example.com' ); // Raw execute for mutations const affected = await db.$executeRaw` UPDATE "User" SET name = 'Anonymous' WHERE name IS NULL `; // Returns: number of affected rows ``` -------------------------------- ### Complex Queries with Kysely Query Builder Source: https://context7.com/zenstackhq/zenstack/llms.txt Access the underlying Kysely query builder via `$qb` to construct complex queries involving joins, custom selections, and raw SQL expressions. Use `$expr` to mix ORM filters with Kysely expressions. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { sql } from 'kysely'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Access Kysely query builder via $qb const complexQuery = await db.$qb .selectFrom('User') .innerJoin('Post', 'Post.authorId', 'User.id') .select([ 'User.id', 'User.name', sql`count(${sql.ref('Post.id')})`.as('postCount'), ]) .where('User.role', '=', 'ADMIN') .groupBy(['User.id', 'User.name']) .having(sql`count(${sql.ref('Post.id')})`, '>', 5) .orderBy('postCount', 'desc') .execute(); // Mix ORM filters with Kysely expressions using $expr const usersWithCustomFilter = await db.user.findMany({ where: { role: 'USER', $expr: (eb) => eb('email', 'like', '%@example.com'), }, }); ``` -------------------------------- ### Associate .zmodel files with ZenStack V2 language Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/ide/vscode/README.md Add this to your .vscode/settings.json to enable ZenStack V2 language features for .zmodel files. Use this if you need to use both versions side-by-side. ```json { "files.associations": { "*.zmodel": "zmodel" } } ``` -------------------------------- ### Express Usage with ZenStackMiddleware Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/server/README.md Integrate ZenStackMiddleware into your Express application to expose your ORM as an API. Ensure necessary imports are included. ```typescript import express from 'express'; import { ZenStackMiddleware } from '@zenstackhq/server/express'; import { RPCApiHandler } from '@zenstackhq/server/api'; const app = express(); app.use('/api/model', ZenStackMiddleware({...})); ``` -------------------------------- ### Generate Type-Safe React Hooks with TanStack Query Source: https://context7.com/zenstackhq/zenstack/llms.txt Create custom React hooks for data fetching using TanStack Query and ZenStack. Ensure your hooks are type-safe by importing the schema. The QuerySettingsProvider requires an endpoint configuration. ```typescript // hooks/useZenStack.ts import { useClientQueries, QuerySettingsProvider } from '@zenstack/tanstack-query/react'; import { schema } from '@/zenstack/schema'; // Create hooks from schema export const useZenStack = () => useClientQueries(schema); ``` ```typescript // App.tsx - Provider setup import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient(); function App() { return ( ); } ``` -------------------------------- ### Perform CRUD Create Operations Source: https://context7.com/zenstackhq/zenstack/llms.txt Execute record creation using the Prisma-compatible API, including support for nested relations, connection to existing records, and batch inserts. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; import { schema } from './zenstack/schema'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Create a single user const user = await db.user.create({ data: { email: 'john@example.com', name: 'John Doe', role: 'USER', }, }); // Returns: { id: 'clx...', email: 'john@example.com', name: 'John Doe', ... } // Create with nested relation (create post with user) const userWithPosts = await db.user.create({ data: { email: 'jane@example.com', name: 'Jane Doe', posts: { create: [ { title: 'First Post', content: 'Hello World', published: true }, { title: 'Draft Post', content: 'Work in progress', published: false }, ], }, }, include: { posts: true }, }); // Create with relation connection const post = await db.post.create({ data: { title: 'Connected Post', content: 'Content here', author: { connect: { id: user.id } }, }, include: { author: true }, }); // Create many records (batch insert) const result = await db.user.createMany({ data: [ { email: 'user1@example.com', name: 'User 1' }, { email: 'user2@example.com', name: 'User 2' }, ], skipDuplicates: true, }); // Returns: { count: 2 } // Create many and return created records (not available in MySQL) const createdUsers = await db.user.createManyAndReturn({ data: [ { email: 'user3@example.com', name: 'User 3' }, { email: 'user4@example.com', name: 'User 4' }, ], select: { id: true, email: true }, }); // Returns: [{ id: '...', email: 'user3@example.com' }, ...] ``` -------------------------------- ### Express Integration Middleware Source: https://context7.com/zenstackhq/zenstack/llms.txt Create Express middleware for automatic CRUD API endpoints using @zenstack/server/express. ```typescript import express from 'express'; import { ZenStackMiddleware } from '@zenstack/server/express'; import { RestApiHandler } from '@zenstack/server/api'; import { db } from './db'; import { schema } from './zenstack/schema'; const app = express(); app.use(express.json()); // Mount ZenStack middleware for CRUD API app.use( '/api/model', ZenStackMiddleware({ apiHandler: new RestApiHandler({ schema, endpoint: 'http://localhost:3000/api/model', pageSize: 50, // Default pagination size }), getClient: (req, res) => { // Get authenticated user from request const userId = req.headers['x-user-id'] as string; if (userId) { return db.$setAuth({ id: userId }); } return db; }, sendResponse: true, // Directly send response (default) }) ); // RESTful API endpoints are now available: // GET /api/model/user - List users // GET /api/model/user/:id - Get user by ID // POST /api/model/user - Create user // PUT /api/model/user/:id - Update user // DELETE /api/model/user/:id - Delete user app.listen(3000, () => console.log('Server running on port 3000')); ``` -------------------------------- ### Implement Computed Field with Kysely Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/orm/README.md Provides the implementation for a computed field 'emailDomain' when creating a ZenStack client. This implementation uses Kysely's expression builder to define the SQL logic for extracting the domain from an email address. ```typescript import { createClient } from '@zenstackhq/orm'; const db = createClient({ computedFields: { User: { emailDomain: (eb) => // build SQL expression: substr(email, instr(email, '@') + 1) eb.fn('substr', [eb.ref('email'), eb(eb.fn('instr', [eb.ref('email'), eb.val('@')]), '+', 1)]), }, }, }); ``` -------------------------------- ### Next.js App Router API Route Source: https://context7.com/zenstackhq/zenstack/llms.txt Set up automatic CRUD API routes for Next.js App Router. Requires @zenstack/server/next and @zenstack/server/api. ```typescript import { NextRequestHandler } from '@zenstack/server/next'; import { RestApiHandler } from '@zenstack/server/api'; import { db } from '@/lib/db'; import { schema } from '@/zenstack/schema'; import { getServerSession } from 'next-auth'; const handler = NextRequestHandler({ useAppDir: true, apiHandler: new RestApiHandler({ schema, endpoint: 'http://localhost:3000/api/model', }), getClient: async (req) => { const session = await getServerSession(); if (session?.user) { return db.$setAuth({ id: session.user.id }); } return db; }, }); export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE }; ``` -------------------------------- ### Associate .zmodel files with ZenStack V3 language Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/ide/vscode/README.md Add this to your .vscode/settings.json to enable ZenStack V3 language features for .zmodel files. ```json { "files.associations": { "*.zmodel": "zmodel-v3" } } ``` -------------------------------- ### Complex Query with Kysely Expression Builder Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/orm/README.md Demonstrates using the $expr key within a 'where' clause to incorporate Kysely expression builder for complex filtering, such as pattern matching on email addresses. ```typescript db.user.findMany({ where: { role: 'USER', // `eb` is Kysely expression builder, fully typed $expr: (eb) => eb('email', 'like', '%@zenstack.dev'), }, }); ``` -------------------------------- ### Find First Matching Record Source: https://context7.com/zenstackhq/zenstack/llms.txt Use `findFirst` to retrieve the first record that matches the specified criteria, optionally ordered. ```typescript const firstAdmin = await db.user.findFirst({ where: { role: 'ADMIN' }, orderBy: { createdAt: 'desc' }, }); ``` -------------------------------- ### Generate TypeScript Schema Source: https://github.com/zenstackhq/zenstack/blob/dev/CLAUDE.md Compile ZModel schema files into TypeScript. Run this after modifying schema files. ```bash npx zenstack generate ``` -------------------------------- ### Upsert (Update or Create) Source: https://context7.com/zenstackhq/zenstack/llms.txt Use `upsert` to either update an existing record if it matches the `where` clause or create a new one if it does not. ```typescript const upsertedUser = await db.user.upsert({ where: { email: 'maybe-new@example.com' }, update: { name: 'Existing User Updated' }, create: { email: 'maybe-new@example.com', name: 'New User', }, }); ``` -------------------------------- ### Utilize Generated React Hooks for Data Fetching and Mutations Source: https://context7.com/zenstackhq/zenstack/llms.txt Demonstrates various TanStack Query hooks generated by ZenStack, including findMany, findUnique, suspense, infinite, count, create, update, and delete operations. Mutations automatically invalidate related queries upon success. ```tsx // components/UserList.tsx import { useZenStack } from '@/hooks/useZenStack'; function UserList() { const { user, post } = useZenStack(); // Query hooks const { data: users, isLoading, error } = user.useFindMany({ where: { role: 'USER' }, include: { posts: true }, orderBy: { createdAt: 'desc' }, }); // Single record query const { data: singleUser } = user.useFindUnique({ where: { id: 'user-id' }, }); // Suspense query (throws promise for Suspense boundary) const { data: suspenseUsers } = user.useSuspenseFindMany(); // Infinite query for pagination const { data: infinitePosts, fetchNextPage, hasNextPage, } = post.useInfiniteFindMany( { take: 10 }, { getNextPageParam: (lastPage, pages) => ({ skip: pages.length * 10, take: 10, }), } ); // Count query const { data: userCount } = user.useCount({ where: { role: 'ADMIN' }, }); // Mutation hooks const createUser = user.useCreate({ onSuccess: () => { // Automatically invalidates related queries }, }); const updateUser = user.useUpdate(); const deleteUser = user.useDelete(); // Call mutations const handleCreate = async () => { await createUser.mutateAsync({ data: { email: 'new@example.com', name: 'New User' }, }); }; const handleUpdate = async (id: string) => { await updateUser.mutateAsync({ where: { id }, data: { name: 'Updated Name' }, }); }; const handleDelete = async (id: string) => { await deleteUser.mutateAsync({ where: { id }, }); }; if (isLoading) return
Loading...
; if (error) return
Error: {error.message}
; return (
{users?.map((u) => (
{u.name} - {u.posts?.length} posts
))}
); } ``` -------------------------------- ### Generate ZenStack Schema Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/nuxt/README.md Generates the TypeScript schema files from the ZenStack model definition. This command is used to update the generated types and code based on your schema. ```bash pnpm generate ``` -------------------------------- ### Nested Selection with Relations Source: https://context7.com/zenstackhq/zenstack/llms.txt Combine `select` and `include` to specify which fields to retrieve from both the main records and their relations. ```typescript const postsWithAuthorName = await db.post.findMany({ select: { title: true, author: { select: { name: true, email: true }, }, }, }); ``` -------------------------------- ### Configure ZenStack V3 as default formatter Source: https://github.com/zenstackhq/zenstack/blob/dev/packages/ide/vscode/README.md Specify ZenStack V3 as the default formatter for .zmodel files within your .vscode/settings.json, especially when using Prettier. ```json { "[zmodel-v3]": { "editor.defaultFormatter": "zenstack.zenstack-v3" } } ``` -------------------------------- ### Count Relations Source: https://context7.com/zenstackhq/zenstack/llms.txt Use `include` with `_count` to retrieve the count of related records for each main record. ```typescript const usersWithPostCount = await db.user.findMany({ include: { _count: { select: { posts: true }, }, }, }); ``` -------------------------------- ### Group and Aggregate Data Source: https://context7.com/zenstackhq/zenstack/llms.txt Use the `groupBy` method to group records by specified fields and perform aggregate calculations on each group. You can filter groups using the `having` clause and order the results. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Group by with aggregation const postsByAuthor = await db.post.groupBy({ by: ['authorId'], _count: { _all: true }, _avg: { viewCount: true }, having: { _count: { _all: { gt: 5 } }, }, orderBy: { _count: { _all: 'desc' }, }, }); // Returns: [{ authorId: '...', _count: { _all: 10 }, _avg: { viewCount: 200 } }, ...] // Group by multiple fields const postsByAuthorAndStatus = await db.post.groupBy({ by: ['authorId', 'published'], _count: true, }); ``` -------------------------------- ### Include Relations Source: https://context7.com/zenstackhq/zenstack/llms.txt Use the `include` option in `findMany` to fetch related records along with the main records, with optional filtering and ordering for included relations. ```typescript const usersWithPosts = await db.user.findMany({ include: { posts: { where: { published: true }, orderBy: { createdAt: 'desc' }, take: 5, }, profile: true, }, }); ``` -------------------------------- ### CRUD Operations - Read Source: https://context7.com/zenstackhq/zenstack/llms.txt Query records with filtering, sorting, pagination, field selection, and relation inclusion. ```APIDOC ## CRUD Operations - Read Query records with filtering, sorting, pagination, field selection, and relation inclusion. ### Find Unique Record Find a single record by its unique identifier (ID or unique field). **Method**: `findUnique` **Example**: ```typescript const user = await db.user.findUnique({ where: { id: 'user-id' }, }); const userByEmail = await db.user.findUnique({ where: { email: 'john@example.com' }, }); ``` ### Find Unique or Throw Find a single record by its unique identifier, throwing an error if not found. **Method**: `findUniqueOrThrow` **Example**: ```typescript const userOrThrow = await db.user.findUniqueOrThrow({ where: { id: 'user-id' }, }); ``` ### Find First Matching Record Find the first record that matches the specified criteria. **Method**: `findFirst` **Example**: ```typescript const firstAdmin = await db.user.findFirst({ where: { role: 'ADMIN' }, orderBy: { createdAt: 'desc' }, }); ``` ### Find Many Records Find multiple records with complex filtering, sorting, and pagination. **Method**: `findMany` **Parameters**: - `where`: Filtering conditions. - `orderBy`: Sorting criteria. - `skip`: Number of records to skip. - `take`: Number of records to take. **Example**: ```typescript const posts = await db.post.findMany({ where: { published: true, author: { role: 'ADMIN', }, OR: [ { title: { contains: 'TypeScript' } }, { content: { contains: 'tutorial' } }, ], createdAt: { gte: new Date('2024-01-01') }, }, orderBy: [ { createdAt: 'desc' }, { title: 'asc' }, ], skip: 0, take: 10, }); ``` ### Field Selection (`select`) Retrieve only specific fields from the records. **Method**: `findMany` with `select` option **Example**: ```typescript const userNames = await db.user.findMany({ select: { id: true, name: true, email: true, }, }); ``` ### Field Exclusion (`omit`) Retrieve all fields except for the specified ones. **Method**: `findMany` with `omit` option **Example**: ```typescript const usersWithoutEmail = await db.user.findMany({ omit: { email: true }, }); ``` ### Include Relations Retrieve related records along with the main records. **Method**: `findMany` with `include` option **Example**: ```typescript const usersWithPosts = await db.user.findMany({ include: { posts: { where: { published: true }, orderBy: { createdAt: 'desc' }, take: 5, }, profile: true, }, }); ``` ### Nested Selection with Relations Select specific fields from related records. **Method**: `findMany` with nested `select` in `include` **Example**: ```typescript const postsWithAuthorName = await db.post.findMany({ select: { title: true, author: { select: { name: true, email: true }, }, }, }); ``` ### Count Relations (`_count`) Count the number of related records. **Method**: `findMany` with `_count` in `include` **Example**: ```typescript const usersWithPostCount = await db.user.findMany({ include: { _count: { select: { posts: true }, }, }, }); ``` ### Check if Record Exists (`exists`) Check if any record matches the given criteria. **Method**: `exists` **Returns**: `true` or `false` **Example**: ```typescript const exists = await db.user.exists({ where: { email: 'john@example.com' }, }); ``` ### Distinct Values Retrieve unique values for specified fields. **Method**: `findMany` with `distinct` option **Example**: ```typescript const uniqueRoles = await db.user.findMany({ distinct: ['role'], select: { role: true }, }); ``` ``` -------------------------------- ### Next.js Pages Router API Route Source: https://context7.com/zenstackhq/zenstack/llms.txt Set up automatic CRUD API routes for Next.js Pages Router. Requires @zenstack/server/next and @zenstack/server/api. ```typescript import { NextRequestHandler } from '@zenstack/server/next'; import { RestApiHandler } from '@zenstack/server/api'; import { db } from '@/lib/db'; import { schema } from '@/zenstack/schema'; export default NextRequestHandler({ useAppDir: false, apiHandler: new RestApiHandler({ schema, endpoint: 'http://localhost:3000/api/model', }), getClient: async (req, res) => { // Get user from session/cookie return db; }, }); ``` -------------------------------- ### Sequential Transactions Source: https://context7.com/zenstackhq/zenstack/llms.txt Execute a predefined array of database operations sequentially within a transaction. The results are returned as an array in the same order as the operations. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Sequential transaction (array of operations) const [user, post] = await db.$transaction([ db.user.create({ data: { email: 'seq@example.com', name: 'Sequential User' }, }), db.post.create({ data: { title: 'Sequential Post', content: 'Content', authorId: 'existing-user-id', }, }), ]); ``` -------------------------------- ### Define Server-Side Computed Field Source: https://github.com/zenstackhq/zenstack/blob/dev/samples/orm/README.md Shows how to define a computed field 'emailDomain' in a ZModel using the '@computed' attribute and provide its implementation using Kysely's expression builder for server-side evaluation. ```prisma model User { ... /// Domain of the email address emailDomain String @computed } ``` -------------------------------- ### Find Many Records with Complex Filtering Source: https://context7.com/zenstackhq/zenstack/llms.txt Use `findMany` with `where`, `orderBy`, `skip`, and `take` to query multiple records with advanced filtering and pagination. ```typescript const posts = await db.post.findMany({ where: { published: true, author: { role: 'ADMIN', }, OR: [ { title: { contains: 'TypeScript' } }, { content: { contains: 'tutorial' } }, ], createdAt: { gte: new Date('2024-01-01') }, }, orderBy: [ { createdAt: 'desc' }, { title: 'asc' }, ], skip: 0, take: 10, }); ``` -------------------------------- ### Field Selection with Select Source: https://context7.com/zenstackhq/zenstack/llms.txt Use the `select` option in `findMany` to retrieve only specific fields from the records. ```typescript const userNames = await db.user.findMany({ select: { id: true, name: true, email: true, }, }); ``` -------------------------------- ### Find Unique Record or Throw Source: https://context7.com/zenstackhq/zenstack/llms.txt Use `findUniqueOrThrow` to retrieve a single record by its unique identifier, throwing an error if no record is found. ```typescript const userOrThrow = await db.user.findUniqueOrThrow({ where: { id: 'user-id' }, }); ``` -------------------------------- ### Interactive Transactions Source: https://context7.com/zenstackhq/zenstack/llms.txt Execute a series of database operations atomically within a callback function using `$transaction`. If any operation within the callback fails, all changes are rolled back. ```typescript import { ZenStackClient } from '@zenstackhq/orm'; const db = new ZenStackClient(schema, { /* dialect config */ }); // Interactive transaction with callback const result = await db.$transaction(async (tx) => { // All operations use the same transaction context const user = await tx.user.create({ data: { email: 'new@example.com', name: 'New User' }, }); const post = await tx.post.create({ data: { title: 'First Post', content: 'Content', authorId: user.id, }, }); // If any operation fails, all changes are rolled back await tx.user.update({ where: { id: user.id }, data: { name: 'Updated Name' }, }); return { user, post }; }); ```