diff --git a/docs/guides/check-permission.md b/docs/guides/check-permission.md index eddeb7c1..7f86744d 100644 --- a/docs/guides/check-permission.md +++ b/docs/guides/check-permission.md @@ -188,6 +188,7 @@ ZenStack uses the [logic-solver](https://www.npmjs.com/package/logic-solver) pac - Array fields are not supported. - Relation fields are not supported. - Collection predicates are not supported. +- The API is not supported on edge runtime (e.g., Cloudflare Workers or Vercel Edge). You can still use the `check` API even if your access policies use these unsupported features. Boolean components containing unsupported features are ignored during SAT solving by being converted to free variables, which can be assigned either `true` or `false` in a solution. diff --git a/docs/guides/edge.md b/docs/guides/edge.md index b918ba87..19589a51 100644 --- a/docs/guides/edge.md +++ b/docs/guides/edge.md @@ -98,7 +98,7 @@ To workaround this problem, add the following to the "package.json" file to avoi ## Cloudflare Workers -You can use ZenStack-enhanced `PrismaClient` in Cloudflare Workers. Here's an example for using with a Neon database. It's recommended to import `enhance` from `@zenstackhq/runtime/edge` instead of `@zenstackhq/runtime`. Although these two modules are identical today, they may diverge in the future due to limitations of the edge runtime. +You can use ZenStack-enhanced `PrismaClient` in Cloudflare Workers. Here's an example for using with a Neon database. Please make sure to import `enhance` from `@zenstackhq/runtime/edge` instead of `@zenstackhq/runtime`. ```ts import { PrismaClient } from '@prisma/client'; diff --git a/docs/guides/prisma-pulse.md b/docs/guides/prisma-pulse.md new file mode 100644 index 00000000..e8a07265 --- /dev/null +++ b/docs/guides/prisma-pulse.md @@ -0,0 +1,46 @@ +--- +description: Using ZenStack with Prisma Pulse. +sidebar_position: 14 +--- + +# Using with Prisma Pulse (Preview) + +## Introduction + +[Prisma Pulse](https://www.prisma.io/data-platform/pulse) is Prisma's cloud offering that allows you to easily subscribe to real-time data change events from your database. It's a great way to build real-time applications without managing a Change Data Capture (CDC) infrastructure. + +With Prisma Pulse, you can use the new `stream()` API to subscribe to data change events. For example: + +```ts +const stream = await prisma.user.stream() + +for await (const event of stream) { + console.log('just received an event:', event) +} +``` + +The `stream()` API also allows you to filter only the events you're interested in. + +```ts +// Filter for new User records with a non-null value for name +const stream = await prisma.user.stream({ + create: { + name: { not: null }, + }, +}); +``` + +## ZenStack's access policies and Prisma Pulse + +TLDR: it just works! + +ZenStack's access policies seamlessly work with the `stream()` API without additional configuration. You can simply use an enhanced Prisma client to subscribe to data change events, and the access policies will be enforced automatically. + +```ts +const db = enhance(prisma, { user: session.user }); + +// The event stream is automatically filtered by the access policies +const stream = await db.user.stream(...); +``` + +Only events that satisfy the "read" policies will be returned. If there are field-level "read" policies, fields that do not satisfy the policies will be stripped from the event payload. Fields marked with `@omit` are also automatically removed. diff --git a/docs/guides/trpc.mdx b/docs/guides/trpc.mdx index c884cae5..fef990a4 100644 --- a/docs/guides/trpc.mdx +++ b/docs/guides/trpc.mdx @@ -4,6 +4,8 @@ sidebar_position: 7 --- import InitTips from '../_components/_zenstack-init-tips.md'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; # Using With tRPC @@ -33,15 +35,60 @@ First install the trpc package: npm install -D @zenstackhq/trpc@latest ``` +Then, add the following plugin configuration to your ZModel schema. Note that the configuration is different for tRPC v10 and v11: + + + + + ```zmodel title='/schema.zmodel' +plugin trpc { + provider = '@zenstackhq/trpc' + output = 'src/server/routers/generated' +} +``` + + + + +To use with tRPC v11, you need to explicitly set the "version" option to "v11" and add two extra options to let ZenStack know where to import the tRPC's router factory (named `createTRPCRouter`) and procedure factory (named `procedure`) from. For example, with the following settings: +```zmodel title='/schema.zmodel' plugin trpc { provider = '@zenstackhq/trpc' output = 'src/server/routers/generated' + version = 'v11' + importCreateRouter = "../../trpc" + importProcedure = "../../trpc" +} +``` + +The generated tRPC routers will contain code like the following: + +```ts +import { createTRPCRouter } from "../../trpc"; +import { procedure } from "../../trpc"; + +export default function createRouter() { + return createTRPCRouter({ + findMany: procedure.input(...).query(...) => ... + ... + }); } ``` +:::info Why is v11 more complicated? + +tRPC v11 removed several public TypeScript types that our previous way of code generation relied on. To get around this, we have to resort to letting the user tell us where to import the factories from so we can use them without explicitly typing them. + +::: + + + + + + ### 3. Setting up the tRPC context Usually in your tRPC project, you have a function to create a tRPC context. You need to make sure the context contains a `prisma` field that is an instance of Prisma client. The generated tRPC routers use that to talk to the database. @@ -73,18 +120,43 @@ npx zenstack generate You should find a bunch of tRPC routers generated in the output folder, one per each data model. A `createRouter` helper function is also generated, which returns a router instance for all models. You can use it as your top-level tRPC router, or merge it with other routers to form a more complex setup. + + + + ```ts title='/src/server/routers/_app.ts' import { createRouter } from './generated/routers'; const t = initTRPC.context().create(); export const appRouter = createRouter(t.router, t.procedure); +export type AppRouter = typeof appRouter; +``` + + + + +```ts title='/src/server/routers/_app.ts' +import { createRouter } from './generated/routers'; + +const t = initTRPC.context().create(); + +export const procedure = t.publicProcedure; +export const createTRPCRouter = t.createRouter; + +... + +export const appRouter = createRouter(); export type AppRouter = typeof appRouter; ``` + + + + _NOTE_: The ZenStack trpc plugin is based on the awesome work by [Omar Dulaimi](https://github.com/omar-dulaimi/prisma-trpc-generator). --- -Please refer to the [@zenstackhq/trpc](../reference/plugins/trpc) plugin documentation for more details. \ No newline at end of file +Please refer to the [@zenstackhq/trpc](../reference/plugins/trpc) plugin documentation for more details. You can also use the [sample-todo-trpc](https://github.com/zenstackhq/sample-todo-trpc) project as a reference. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 156c0c3b..faf6832b 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -25,11 +25,26 @@ Commands: generate [options] Generates RESTful API and Typescript client for your data model. repl [options] Start a REPL session. format [options] Format a ZenStack schema file. + check [options] Check a ZenStack schema file for syntax or semantic errors. help [command] Display help for a command. ``` ## Sub Commands +### info + +Get information of installed ZenStack and related packages. + +```bash +zenstack info [options] [path] +``` + +#### Arguments + +| Name | Description | Default | +| ---- | ------------ | -------------- | +| path | Project path | current folder | + ### init Initializes an existing project to use ZenStack. @@ -86,6 +101,8 @@ zenstack generate [options] | --------------------- | ------------------------------------------------ | ---------------------- | | --schema | schema file (with extension .zmodel) | ./schema.zmodel | | -o, --output <path> | default output directory for TS/JS files generated by built-in plugins | node_modules/.zenstack | +| --with-plugins | only run specific plugins | | +| --without-plugins | exclude specific plugins | | | --no-default-plugins | do not automatically run built-in plugins | false | | --no-compile | do not compile the output of built-in plugins | false | | --no-version-check | do not check for new versions of ZenStack | false | @@ -218,16 +235,16 @@ You can also specify the ZModel schema location in the "package.json" file of yo } ``` -### info +### check -Get information of installed ZenStack and related packages. +Check a ZenStack schema file for syntax or semantic errors. You can use the CLI's exit code to determine if the schema is valid. ```bash -zenstack info [options] [path] +zenstack check [options] ``` -#### Arguments +#### Options -| Name | Description | Default | -| ---- | ------------ | -------------- | -| path | Project path | current folder | +| Name | Description | Default | +| --------------------- | ------------------------------------------------ | ---------------------- | +| --schema | schema file (with extension .zmodel) | ./schema.zmodel | diff --git a/docs/reference/plugins/trpc.md b/docs/reference/plugins/trpc.md index 135fec88..e8ee4409 100644 --- a/docs/reference/plugins/trpc.md +++ b/docs/reference/plugins/trpc.md @@ -24,7 +24,9 @@ npm install --save-dev @zenstackhq/trpc | Name | Type | Description | Required | Default | | ------ | ------ | ---------------- | -------- | ------- | | output | String | Output directory (relative to the path of ZModel) | Yes | | -| generateModels | String, String[] | Array or comma separated string for the models to generate routers for. | No | All models | +| version | String | tRPC version to target - "v10" or "v11". "v11" support is still in preview. | No | v10 | +| importCreateRouter | String | Only needed when "version" is set to "v11". This option tells the code generator where to import the `createTRPCRouter` tRPC router factory object from. | Yes (v11) | | +| importProcedure | String | Only needed when "version" is set to "v11". This option tells the code generator where to import the `procedure` tRPC procedure factory object from. | Yes (v11) | || generateModels | String, String[] | Array or comma separated string for the models to generate routers for. | No | All models | | generateModelActions | String, String[] | Array or comma separated string for actions to generate for each model: `create`, `findUnique`, `update`, etc. | No | All supported Prisma actions | | generateClientHelpers | String, String[] | Array or comma separated string for the types of client helpers to generate. Supported values: "react" or "next". See [here](#client-helpers) for more details. | No | | | zodSchemasImport | String | Import path for the generated zod schemas. The trpc plugin relies on the `@core/zod` plugin to generate zod schemas for input validation. If you set a custom output location for the zod schemas, you can use this option to override the import path. | No | @zenstackhq/runtime/zod | diff --git a/docs/reference/prisma-client-ext.md b/docs/reference/prisma-client-ext.md index 63358e8f..654c20b1 100644 --- a/docs/reference/prisma-client-ext.md +++ b/docs/reference/prisma-client-ext.md @@ -14,6 +14,10 @@ ZenStack's enhancement to PrismaClient not only alters its existing APIs' behavi This API is added to each model in the PrismaClient. +:::warning +The API is not supported on edge runtime (e.g., Cloudflare Workers or Vercel Edge). You'll get a runtime error when calling it. +::: + #### Description Checks if the current user is allowed to perform the specified operation on the model based on the access policies in ZModel. The check is done via pure logical inference and doesn't query the database. diff --git a/docs/reference/zmodel-language.md b/docs/reference/zmodel-language.md index 0affbb94..4d525295 100644 --- a/docs/reference/zmodel-language.md +++ b/docs/reference/zmodel-language.md @@ -1643,10 +1643,10 @@ Attributes `@trim`, `@lower`, and `@upper` are actually "transformation" instead ### Model-level validation attributes -You can use the `@@validate` attribute to attach validation rules to a model. +You can use the `@@validate` attribute to attach validation rules to a model. Use the `message` parameter to provide an optional custom error message, and the `path` parameter to provide an optional path to the field that caused the error. ``` -@@validate(_ value: Boolean, _ message: String?) +@@validate(_ value: Boolean, _ message: String?, _ path: String[]?) ``` Model-level rules can reference multiple fields, use relation operators (`==`, `!=`, `>`, `>=`, `<`, `<=`) to compare fields, use boolean operators (`&&`, `||`, and `!`) to compose conditions, and can use the following functions to evaluate conditions for fields: