From 657a9c2db58698378acbd87b267699635e559fe8 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 27 Mar 2024 15:54:51 -0700
Subject: [PATCH 1/4] docs: add NestJS tutorial
---
docs/quick-start/_access-policy.md | 4 +-
docs/quick-start/_zenstack-init-tips.md | 2 +-
docs/quick-start/backend.mdx | 2 +-
docs/quick-start/nestjs.mdx | 476 ++++++++++++++++++++++++
docs/upgrade.md | 4 +
docusaurus.config.js | 1 +
6 files changed, 485 insertions(+), 4 deletions(-)
create mode 100644 docs/quick-start/nestjs.mdx
diff --git a/docs/quick-start/_access-policy.md b/docs/quick-start/_access-policy.md
index 78525599..ef0a3e40 100644
--- a/docs/quick-start/_access-policy.md
+++ b/docs/quick-start/_access-policy.md
@@ -1,8 +1,8 @@
-:::tip
+:::info
By default, all operations are denied for a model. You can use `@@allow` attribute to open up some permissions.
-`@@allow` takes two parameters, the first is operation: create/read/update/delete. You can use a comma separated string to pass multiple operations, or use 'all' to abbreviate all operations. The second parameter is a boolean expression verdicting if the rule should be activated.
+`@@allow` takes two parameters, the first is operation: create/read/update/delete. You can use a comma separated string to pass multiple operations, or use 'all' to abbreviate all operations. The second parameter is a boolean expression that verdicts if the rule should be activated.
Similarly, `@@deny` can be used to explicitly turn off some operations. It has the same syntax as `@@allow` but the opposite effect.
diff --git a/docs/quick-start/_zenstack-init-tips.md b/docs/quick-start/_zenstack-init-tips.md
index fde01723..472efd3f 100644
--- a/docs/quick-start/_zenstack-init-tips.md
+++ b/docs/quick-start/_zenstack-init-tips.md
@@ -1,4 +1,4 @@
-:::tip
+:::info
The command installs a few NPM dependencies. If the project already has a Prisma schema at `prisma/schema.prisma`, it's copied over to `schema.zmodel`. Otherwise, a sample `schema.zmodel` file is created.
diff --git a/docs/quick-start/backend.mdx b/docs/quick-start/backend.mdx
index 95d41e15..e3c10468 100644
--- a/docs/quick-start/backend.mdx
+++ b/docs/quick-start/backend.mdx
@@ -1,6 +1,6 @@
---
description: Steps for using ZenStack only in backend development.
-sidebar_position: 7
+sidebar_position: 8
---
import InitTips from './_zenstack-init-tips.md';
diff --git a/docs/quick-start/nestjs.mdx b/docs/quick-start/nestjs.mdx
new file mode 100644
index 00000000..65121634
--- /dev/null
+++ b/docs/quick-start/nestjs.mdx
@@ -0,0 +1,476 @@
+---
+title: NestJS
+description: Steps for using ZenStack in a NestJS application.
+sidebar_position: 7
+---
+
+import InitTips from './_zenstack-init-tips.md';
+import AccessPolicy from './_access-policy.md';
+
+# Get Started With NestJS
+
+NestJS is one of the most popular Node.js/TypeScript backend frameworks for building APIs. ZenStack provides a module for easily integrating with NestJS applications that use Prisma ORM. With the integration, you'll have access to an enhanced Prisma service with built-in access control, while continue enjoying the same Prisma APIs that you're familiar with.
+
+Let's see how it works by creating a simple blogging API. You can find the final build result [here](https://github.com/zenstackhq/docs-tutorial-nestjs).
+
+## Requirements
+
+Our target app should meet the following requirements:
+
+1. Users can create posts for themselves.
+1. Post owner can update their own posts.
+1. Users cannot make changes to posts that do not belong to them.
+1. Published posts can be viewed by everyone.
+
+Let's get started 🚀.
+
+## Prerequisite
+
+1. Make sure you have Node.js 18 or above installed.
+1. Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=zenstack.zenstack) for editing data models.
+
+## Building the app
+
+### 1. Create a new NestJS project
+
+```bash
+npx @nestjs/cli@latest new -p npm my-blog-app
+cd my-blog-app
+```
+
+### 2. Set up Prisma
+
+```bash
+npm install -D prisma
+npx prisma init
+```
+
+This will create a Prisma schema under `prisma/schema.prisma`. Replace its content with the following:
+
+```zmodel title="/prisma/schema.prisma"
+datasource db {
+ provider = "sqlite"
+ url = "file:./dev.db"
+}
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+model User {
+ id Int @id() @default(autoincrement())
+ name String
+ posts Post[]
+}
+
+model Post {
+ id Int @id() @default(autoincrement())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt()
+ title String
+ published Boolean @default(false)
+ author User @relation(fields: [authorId], references: [id])
+ authorId Int
+}
+```
+
+Now, generate `PrismaClient` and push the schema to the database:
+
+```bash
+npx prisma generate
+npx prisma db push
+```
+
+Create a `PrismaService` which will be injected into the API controllers later.
+
+```ts title="/src/prisma.service.ts"
+import { Injectable, OnModuleInit } from '@nestjs/common';
+import { PrismaClient } from '@prisma/client';
+
+@Injectable()
+export class PrismaService extends PrismaClient implements OnModuleInit {
+ async onModuleInit() {
+ await this.$connect();
+ }
+}
+```
+
+Finally, add the `PrismaService` to the app module as a provider:
+
+```ts title="/src/app.module.ts"
+import { PrismaService } from './prisma.service';
+
+@Module({
+ imports: [],
+ controllers: [AppController],
+ providers: [PrismaService],
+})
+export class AppModule {}
+```
+
+### 3. Create CRUD controllers
+
+Now let's create the CRUD API controller for `User` and `Post` models. In a real application, you'll want to have `UserService` and `PostService` to encapsulate database operations. For simplicity, we'll put everything in the controller here.
+
+```ts title="/src/app.controller.ts"
+import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common';
+import { PrismaService } from './prisma.service';
+
+@Controller()
+export class AppController {
+ constructor(private readonly prismaService: PrismaService) {}
+
+ @Post('users')
+ async signup(@Body() userData: { name: string }) {
+ return this.prismaService.user.create({ data: userData });
+ }
+
+ @Get('posts')
+ async getAllPosts() {
+ return this.prismaService.post.findMany();
+ }
+
+ @Post('posts')
+ async createDraft(@Body() postData: { title: string; authorId: number }) {
+ return this.prismaService.post.create({
+ data: postData,
+ });
+ }
+
+ @Put('posts/publish/:id')
+ async publishPost(@Param('id') id: string) {
+ return this.prismaService.post.update({
+ where: { id: Number(id) },
+ data: { published: true },
+ });
+ }
+}
+```
+
+Now, we can start the dev server:
+
+```bash
+npm run start:dev
+```
+
+Let's make a few requests to create a user and two posts:
+
+```bash
+curl -X POST -H "Content-Type: application/json" -d '{"name": "Joey"}' localhost:3000/users
+curl -X POST -H "Content-Type: application/json" -d '{"title": "My first post", "authorId": 1}' localhost:3000/posts
+curl -X POST -H "Content-Type: application/json" -d '{"title": "My second post", "authorId": 1}' localhost:3000/posts
+curl localhost:3000/posts
+```
+
+The result should look like:
+
+```json
+[
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:27.289Z",
+ "id" : 1,
+ "published" : false,
+ "title" : "My first post",
+ "updatedAt" : "2024-03-27T18:16:27.289Z"
+ },
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:35.302Z",
+ "id" : 2,
+ "published" : false,
+ "title" : "My second post",
+ "updatedAt" : "2024-03-27T18:16:35.302Z"
+ }
+]
+```
+
+### 4. Set up authentication
+
+Our basic CRUD APIs are up and running. However it's not secured yet. Protecting an API involves two parts: authentication (identifying who's making the request) and authorization (deciding if the requester is allowed to perform the operation).
+
+Let's deal with authentication first. NestJS has [detailed documentation](https://docs.nestjs.com/security/authentication) for implementing authentication. In this guide, we'll simply use a fake one that directly passes user ID in a HTTP header. To allow services and controllers to access the authenticatd user, we'll use the [nestjs-cls](https://www.npmjs.com/package/nestjs-cls) package to put the user information into Node.js's [AsyncLocalStorage](https://nodejs.org/api/async_context.html#async_context_class_asynclocalstorage).
+
+First, install the `nestjs-cls` package:
+
+```bash
+npm install nestjs-cls
+```
+
+Then, mount the CLS module:
+
+```ts title="/src/app.module.ts"
+import { ClsModule, ClsService } from 'nestjs-cls';
+
+@Module({
+ imports: [
+ // highlight-start
+ ClsModule.forRoot({
+ global: true,
+ middleware: {
+ mount: true,
+ },
+ }),
+ // highlight-end
+ ],
+ ...
+})
+export class AppModule {}
+```
+
+Now, let's create a NestJS interceptor to extract the user ID from the HTTP header and put it into the CLS context:
+
+```ts title="/src/auth.interceptor.ts"
+import {
+ CallHandler,
+ ExecutionContext,
+ Injectable,
+ NestInterceptor,
+} from '@nestjs/common';
+import { ClsService } from 'nestjs-cls';
+
+@Injectable()
+export class AuthInterceptor implements NestInterceptor {
+ constructor(private readonly cls: ClsService) {}
+
+ async intercept(context: ExecutionContext, next: CallHandler) {
+ const request = context.switchToHttp().getRequest();
+ const userId = request.headers['x-user-id'];
+ if (userId) {
+ this.cls.set('auth', { id: Number(userId) });
+ }
+ return next.handle();
+ }
+}
+```
+
+Then, add the interceptor to `AppModule`:
+
+```ts title="/src/app.module.ts"
+import { APP_INTERCEPTOR } from '@nestjs/core';
+import { AuthInterceptor } from './auth.interceptor';
+
+@Module({
+ ...
+
+ providers: [
+ PrismaService,
+ // highlight-start
+ {
+ provide: APP_INTERCEPTOR,
+ useClass: AuthInterceptor,
+ },
+ // highlight-end
+ ],
+})
+export class AppModule {}
+```
+
+Now we will be able to inject the `ClsService` into the controllers and services as needed to fetch the current authenticated user.
+
+### 5. Set up ZenStack
+
+ZenStack allows you to define access policies inside your data schema. Let's install it first.
+
+```bash
+npx zenstack@next init
+```
+
+
+
+Add the following access policies to the `User` and `Post` models:
+
+```zmodel title='/schema.zmodel'
+model User {
+ id Int @id() @default(autoincrement())
+ name String
+ posts Post[]
+
+ // highlight-start
+ // anyone can sign up, and user profiles are public
+ @@allow('create,read', true)
+
+ // users have full access to their own profile
+ @@allow('all', auth() == this)
+ // highlight-end
+}
+
+model Post {
+ id Int @id() @default(autoincrement())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt()
+ title String
+ published Boolean @default(false)
+ author User? @relation(fields: [authorId], references: [id])
+ authorId Int?
+
+ // highlight-start
+ // author has full access
+ @@allow('all', auth() == author)
+
+ // published posts are readable to all
+ @@allow('read', published)
+ // highlight-end
+}
+```
+
+
+
+Now regenerate `PrismaClient` and other supporting files needed by ZenStack:
+
+```bash
+npx zenstack generate
+```
+
+### 6. Use ZenStack in the controller
+
+One of the main things ZenStack does is to create an "enhanced" `PrismaClient` that automatically enforces access policies. To do that, simply call the `enhance` API with an existing client and a user context:
+
+```ts
+const enhancedPrisma = enhance(prisma, { user: ... });
+```
+
+In a NestJS application, since everything is a dependency injection, we need to create the enhanced client in a DI-compatible way. Fortunately, ZenStack offers a module to make such integration easy. First, install the server adapter package:
+
+```bash
+npm install @zenstackhq/server@next
+```
+
+Then, register the `ZenStackModule` onto the app module:
+
+```ts title="/src/app.module.ts"
+import { ZenStackModule } from '@zenstackhq/server/nestjs';
+import { enhance } from '@zenstackhq/runtime';
+
+@Module({
+ imports: [
+ ...
+
+ // highlight-start
+ ZenStackModule.registerAsync({
+ useFactory: (prisma: PrismaService, cls: ClsService) => {
+ return {
+ getEnhancedPrisma: () => enhance(prisma, { user: cls.get('auth') }),
+ };
+ },
+ inject: [PrismaService, ClsService],
+ extraProviders: [PrismaService],
+ }),
+ // highlight-end
+ ],
+
+ ...
+})
+export class AppModule {}
+```
+
+Note that the `ZenStackModule` registration is done with a factory function that returns a config used for creating an enhanced prisma service. The config contains a callback function where you should create and return an enhanced `PrismaClient`. It'll be called each time a Prisma method is invoked. It's important to fetch the auth data inside the callback so that it correctly returns the data bound to the current request context.
+
+:::info
+The enhanced clients are lightweighted Javascript proxies. They are cheap to create and don't incur new connections to the database.
+:::
+
+The `ZenStackModule` provides an enhanced `PrismaService` with the token name `ENHANCED_PRISMA`. You can use both the regular `PrismaService` and enhanced one in your services and controllers. To use the regular prisma client, simply inject the `PrismaService` as usual. To use the enhanced one, inject it with token name `ENHANCED_PRISMA`.
+
+Let's change our controller to use the enhanced prisma service:
+
+```ts title="/src/app.controller.ts"
+import { ENHANCED_PRISMA } from '@zenstackhq/server/nestjs';
+
+@Controller()
+export class AppController {
+ constructor(
+ // highlight-next-line
+ @Inject(ENHANCED_PRISMA) private readonly prismaService: PrismaService,
+ ) {}
+
+ ...
+}
+```
+
+### 7. Test the secured API
+
+Now, restart the dev server, and let's make a few requests to see if the access policies are enforced.
+
+- Listing posts without a user identity should return an empty array:
+
+ ```bash
+ curl localhost:3000/posts
+ ```
+
+ ```
+ []
+ ```
+
+- Listing posts with a user identity should return all posts owned by the user:
+
+ ```bash
+ curl -H "x-user-id:1" localhost:3000/posts
+ ```
+
+ ```json
+ [
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:27.289Z",
+ "id" : 1,
+ "published" : false,
+ "title" : "My first post",
+ "updatedAt" : "2024-03-27T18:16:27.289Z"
+ },
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:35.302Z",
+ "id" : 2,
+ "published" : false,
+ "title" : "My second post",
+ "updatedAt" : "2024-03-27T18:16:35.302Z"
+ }
+ ]
+ ```
+
+- Published posts are readable to all:
+
+ First, publish a post with its owner's identity.
+
+ ```
+ curl -X PUT -H "x-user-id:1" localhost:3000/posts/publish/1
+ ```
+
+ ```json
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:27.289Z",
+ "id" : 1,
+ "published" : true,
+ "title" : "My first post",
+ "updatedAt" : "2024-03-27T18:42:19.043Z"
+ }
+ ```
+
+ Then, list all posts without a user identity:
+
+ ```bash
+ curl localhost:3000/posts
+ ```
+
+ ```json
+ {
+ "authorId" : 1,
+ "createdAt" : "2024-03-27T18:16:27.289Z",
+ "id" : 1,
+ "published" : true,
+ "title" : "My first post",
+ "updatedAt" : "2024-03-27T18:42:19.043Z"
+ }
+ ```
+
+## Wrap up
+
+🎉 Congratulations! You've made a simple but secure blogging API without writing any
+authorization code. Pretty cool, isn't it?
+
+If you have trouble following the building process, you can find the final result
+[here](https://github.com/zenstackhq/docs-tutorial-nestjs). For more details about ZenStack, please refer to the [Reference](../category/reference) and [Guides](../category/recipes) parts of the documentation.
+
+Have fun building cool stuff 🚀!
diff --git a/docs/upgrade.md b/docs/upgrade.md
index 60b9f7c6..8aacae9d 100644
--- a/docs/upgrade.md
+++ b/docs/upgrade.md
@@ -175,6 +175,10 @@ We may introduce a new config file format in the future.
### 3. Server Adapter
+#### HTTP status code `422` is used to represent data validation errors
+
+In V1, when a [data validation](../docs/reference/zmodel-language.md#data-validation) error happens (due to violation of rules represented by `@email`, `@length`, `@@validate` etc.), the server adapters used `403` to represent such error. This is changed in V2 to use `422` to align with the [HTTP status code definition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422).
+
#### The deprecated `useSuperJSON` initialization options is removed
The server adapters always use SuperJSON for serialization and deserialization.
diff --git a/docusaurus.config.js b/docusaurus.config.js
index bb31df38..24badaf6 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -196,6 +196,7 @@ const config = {
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
+ additionalLanguages: ['json'],
},
zoom: {
config: {
From ec77afd4a930d619e97df68ec09759ae0e947d95 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 27 Mar 2024 16:26:40 -0700
Subject: [PATCH 2/4] add nestjs api docs
---
docs/reference/server-adapters/nestjs.mdx | 123 ++++++++++++++++++++++
1 file changed, 123 insertions(+)
create mode 100644 docs/reference/server-adapters/nestjs.mdx
diff --git a/docs/reference/server-adapters/nestjs.mdx b/docs/reference/server-adapters/nestjs.mdx
new file mode 100644
index 00000000..af47ce3a
--- /dev/null
+++ b/docs/reference/server-adapters/nestjs.mdx
@@ -0,0 +1,123 @@
+---
+title: NestJS
+description: Adapter for integrating with NestJS
+sidebar_position: 6
+---
+
+import ErrorHandling from './_error-handling.md';
+import AdapterOptions from './_options.mdx';
+import UsingAPI from './_using-api.mdx';
+
+# NestJS Adapter
+
+The `@zenstackhq/server/nestjs` module provides a quick way to install an ZenStack-enhanced Prisma service as a dependency injection provider onto a [NestJS](https://nestjs.com/) application.
+
+### Installation
+
+```bash
+npm install @zenstackhq/server
+```
+
+### Registering the provider
+
+You can register the enhanced Prisma service by importing the `ZenStackModule` NestJS module.
+
+```ts
+import { ZenStackModule } from '@zenstackhq/server/nestjs';
+import { enhance } from '@zenstackhq/runtime';
+import { PrismaService } from './prisma.service';
+
+@Module({
+ imports: [
+ ZenStackModule.registerAsync({
+ useFactory: (prisma: PrismaService) => {
+ return {
+ getEnhancedPrisma: () => enhance(prisma, { user: ... }),
+ };
+ },
+ inject: [PrismaService],
+ extraProviders: [PrismaService],
+ }),
+ ],
+})
+export class AppModule {}
+```
+
+The `registerAsync` API takes as input a factory function that returns a config used for creating an enhanced prisma service. The config contains a callback function where you should create and return an enhanced `PrismaClient`. It'll be called each time a Prisma method is invoked.
+
+You'll usually pass in a user context when calling `enhance` inside the callback. The way how the user context is fetched depends on your authentication mechanism. You can check the [NestJS quick start guide](../../quick-start/nestjs) for a reference solution.
+
+### Using the enhanced Prisma service
+
+Inside your NestJS controllers or services, you can inject the enhanced Prisma service and use it as you would with the regular Prisma service. Just use the special token name `ENHANCED_PRISMA` when injecting the service.
+
+```ts
+import { ENHANCED_PRISMA } from '@zenstackhq/server/nestjs';
+
+@Controller()
+export class MyController {
+ constructor(
+ @Inject(ENHANCED_PRISMA) private readonly prismaService: PrismaService,
+ ) {}
+
+ ...
+}
+```
+
+You can still use the regular Prisma service by injecting as usual.
+
+### API reference
+
+#### `ZenStackModule.registerAsync`
+
+##### Signature
+
+```ts
+registerAsync(options: ZenStackModuleAsyncOptions): DynamicModule;
+```
+
+##### Parameter `options`
+
+```ts
+interface ZenStackModuleAsyncOptions {
+ /**
+ * Optional list of imported modules that export the providers which are
+ * required in this module.
+ */
+ imports?: Array | DynamicModule | Promise | ForwardReference>;
+
+ /**
+ * Whether the module is global-scoped.
+ */
+ global?: boolean;
+
+ /**
+ * The token to export the enhanced Prisma service. Default is `'ENHANCED_PRISMA'`.
+ */
+ exportToken?: string;
+
+ /**
+ * The factory function to create the enhancement options.
+ */
+ useFactory: (...args: unknown[]) => Promise | ZenStackModuleOptions;
+
+ /**
+ * The dependencies to inject into the factory function.
+ */
+ inject?: FactoryProvider['inject'];
+
+ /**
+ * Extra providers to facilitate dependency injection.
+ */
+ extraProviders?: Provider[];
+}
+```
+
+```ts
+interface ZenStackModuleOptions {
+ /**
+ * A callback for getting an enhanced `PrismaClient`.
+ */
+ getEnhancedPrisma: () => unknown;
+}
+```
From 10a8f3c0b17c48d9cb53165ca018ad8e6ba23de4 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 27 Mar 2024 16:30:24 -0700
Subject: [PATCH 3/4] fix grammar
---
docs/quick-start/_access-policy.md | 8 ++++----
.../version-1.x/quick-start/_access-policy.md | 10 +++++-----
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/docs/quick-start/_access-policy.md b/docs/quick-start/_access-policy.md
index ef0a3e40..fcef0835 100644
--- a/docs/quick-start/_access-policy.md
+++ b/docs/quick-start/_access-policy.md
@@ -1,16 +1,16 @@
:::info
-By default, all operations are denied for a model. You can use `@@allow` attribute to open up some permissions.
+By default, all operations are denied for a model. You can use the `@@allow` attribute to open up some permissions.
`@@allow` takes two parameters, the first is operation: create/read/update/delete. You can use a comma separated string to pass multiple operations, or use 'all' to abbreviate all operations. The second parameter is a boolean expression that verdicts if the rule should be activated.
Similarly, `@@deny` can be used to explicitly turn off some operations. It has the same syntax as `@@allow` but the opposite effect.
-Whether an operation is permitted is determined as the follows:
+Whether an operation is permitted is determined as follows:
1. If any `@@deny` rule evaluates to true, it's denied.
-1. If any `@@allow` rule evaluates to true, it's allowed.
-1. Otherwise, it's denied.
+2. If any `@@allow` rule evaluates to true, it's allowed.
+3. Otherwise, it's denied.
Check out [Understanding Access Policies](/docs/the-complete-guide/part1/access-policy) for more details.
:::
diff --git a/versioned_docs/version-1.x/quick-start/_access-policy.md b/versioned_docs/version-1.x/quick-start/_access-policy.md
index 78525599..276589b4 100644
--- a/versioned_docs/version-1.x/quick-start/_access-policy.md
+++ b/versioned_docs/version-1.x/quick-start/_access-policy.md
@@ -1,16 +1,16 @@
:::tip
-By default, all operations are denied for a model. You can use `@@allow` attribute to open up some permissions.
+By default, all operations are denied for a model. You can use the `@@allow` attribute to open up some permissions.
-`@@allow` takes two parameters, the first is operation: create/read/update/delete. You can use a comma separated string to pass multiple operations, or use 'all' to abbreviate all operations. The second parameter is a boolean expression verdicting if the rule should be activated.
+`@@allow` takes two parameters, the first is operation: create/read/update/delete. You can use a comma separated string to pass multiple operations, or use 'all' to abbreviate all operations. The second parameter is a boolean expression that verdicts if the rule should be activated.
Similarly, `@@deny` can be used to explicitly turn off some operations. It has the same syntax as `@@allow` but the opposite effect.
-Whether an operation is permitted is determined as the follows:
+Whether an operation is permitted is determined as follows:
1. If any `@@deny` rule evaluates to true, it's denied.
-1. If any `@@allow` rule evaluates to true, it's allowed.
-1. Otherwise, it's denied.
+2. If any `@@allow` rule evaluates to true, it's allowed.
+3. Otherwise, it's denied.
Check out [Understanding Access Policies](/docs/the-complete-guide/part1/access-policy) for more details.
:::
From ec1df4e6cc7f57617a8ea5b6699655ba7ebe9a55 Mon Sep 17 00:00:00 2001
From: ymc9 <104139426+ymc9@users.noreply.github.com>
Date: Wed, 27 Mar 2024 16:31:36 -0700
Subject: [PATCH 4/4] fix
---
docs/reference/server-adapters/nestjs.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/reference/server-adapters/nestjs.mdx b/docs/reference/server-adapters/nestjs.mdx
index af47ce3a..b31305d9 100644
--- a/docs/reference/server-adapters/nestjs.mdx
+++ b/docs/reference/server-adapters/nestjs.mdx
@@ -10,7 +10,7 @@ import UsingAPI from './_using-api.mdx';
# NestJS Adapter
-The `@zenstackhq/server/nestjs` module provides a quick way to install an ZenStack-enhanced Prisma service as a dependency injection provider onto a [NestJS](https://nestjs.com/) application.
+The `@zenstackhq/server/nestjs` module provides a quick way to install a ZenStack-enhanced Prisma service as a dependency injection provider onto a [NestJS](https://nestjs.com/) application.
### Installation