@casl/prisma

中文文档
2.2.0发布于 2026/10/28MITTypeScriptESM + CJS构建来源已验证近 30 天下载 81.2万

Allows to query accessible records using Prisma client based on CASL rules

原始使用文档
文档目录12 节
  1. Installation
  2. Usage
  3. Note on subject helper
  4. Note on Prisma Query runtime interpreter
  5. Prisma 7 configuration
  6. Finding Accessible Records
  7. Prisma Client extension
  8. TypeScript support
  9. PrismaQuery
  10. Custom PrismaClient output path (Prisma 7 default)
  11. Want to help?
  12. License

CASL Prisma

@casl/prisma NPM version Support

This package allows to define CASL permissions on Prisma models using Prisma WhereInput. And that brings a lot of power in terms of permission management in SQL world:

  1. We can use Prisma Query to define permissions, no need to learn MongoDB query language anymore.
  2. Additionally, we can ask our SQL database questions like: "Which records can be read?" or "Which records can be updated?".

Installation

npm install @casl/prisma @casl/ability
# or
yarn add @casl/prisma @casl/ability
# or
pnpm add @casl/prisma @casl/ability

@casl/prisma requires Prisma Client 4.16.0 or later. Prisma Client extensions are used by the createCaslExtension helper which is important to add to Prisma Client!

Usage

This package is a bit different from all others because it provides a custom createPrismaAbility factory function that is configured to check permissions using Prisma WhereInput:

import { User, Post, Prisma } from '@prisma/client';
import { Ability, AbilityBuilder, subject, type ForcedSubject, type TaggedSubject } from '@casl/ability';
import { createPrismaAbility, PrismaQuery } from '@casl/prisma';

type AppSubjects =
  | User & ForcedSubject<'User'>
  | Post & ForcedSubject<'Post'>
type AppAbility = Ability<[string, AppSubjects], PrismaQuery, TaggedSubject>;
const { can, cannot, build } = new AbilityBuilder<AppAbility>(createPrismaAbility);

can('read', 'Post', { authorId: 1 });
cannot('read', 'Post', { title: { startsWith: '[WIP]:' } });

const ability = build();
ability.can('read', 'Post');
ability.can('read', subject('Post', { title: '...', authorId: 1 })));

See CASL guide to learn how to define abilities. Everything is the same except of conditions language.

If you generate Prisma Client with the Prisma 7 prisma-client generator, import model types from your generated client path (for example ./prisma/generated/client) and create a small wrapper around @casl/prisma/runtime as shown below.

Note on subject helper

ForcedSubject<'Post'> declares the tag on an object. When your subject union contains only tagged object types, you must specify TaggedSubject as the third generic parameter of Ability or PrismaAbility to also accept subject names such as 'Post' in permission checks:

type AppAbility = Ability<[string, AppSubjects], PrismaQuery, TaggedSubject>;

TaggedSubject uses __caslSubjectType__ by default, matching the subject helper. If you omit the third generic parameter, DefaultSubject preserves the existing behavior: include both the names and the tagged object types explicitly, for example Ability<[string, 'User' | 'Post' | AppSubjects], PrismaQuery>.

Because Prisma returns DTO objects without exposing any type information on it, we need to use subject helper to provide that type manually, so CASL can understand what rules to apply to passed in object.

Unfortunately, there is no easy way to automate this, except of adding additional column to all models. For more details, check this issue.

To get more details about object type detection, please read CASL Subject type detection

Note on Prisma Query runtime interpreter

@casl/prisma uses ucast to interpret Prisma WhereInput in JavaScript runtime. However, there are few caveats:

  • equality of JSON columns is not implemented
  • equality of array/list columns is not implemented (however operators like has, hasSome and hasEvery should be more than enough)
  • when defining conditions on relation, always specify one of operators (every, none, some, is or isNot)

Interpreter throws a ParsingQueryError in cases it receives invalid parameters for query operators or if some operation is not supported.

Prisma 7 configuration

Prisma 7 moves connection details into prisma.config.ts and no longer loads .env files automatically. Add a config next to your schema so prisma generate keeps working:

// prisma.config.ts
import 'dotenv/config';
import { defineConfig } from 'prisma/config';

export default defineConfig({
  schema: './schema.prisma',
  datasource: {
    // fall back to a local URL so generate does not fail in CI
    url: process.env.DATABASE_URL ?? 'postgresql://postgres:postgres@localhost:5432/postgres',
  },
});

If you stay on the legacy prisma-client-js generator, the default @prisma/client import keeps working.

Finding Accessible Records

One nice feature of Prisma and CASL integration is that we can get all records from the database our user has access to. To do this, just use accessibleBy helper function:

// ability is a PrismaAbility instance created in the example above

const accessiblePosts = await prisma.post.findMany({
  where: accessibleBy(ability).ofType('Post')
});

That function accepts Ability instance and action (defaults to read), returns an instance of AccessibleRecords with an ofType method which accepts Prisma model name and returns an object aggregated from permission rules WhereInput.

Important: in case user doesn't have ability to access any posts, .ofType returns a special empty condition. If you pass it directly to Prisma Client without createCaslExtension, Prisma rejects the query because the condition contains an internal marker field. This makes denied access fail closed.

To combine this with business logic conditions, just use AND:

const accessiblePosts = await prisma.post.findMany({
  where: {
    AND: [
      accessibleBy(ability).ofType('Post'),
      { /* business related conditions */ }
    ]
  }
})

For the current Prisma extension implementation, putting accessibleBy(...) first in compound conditions such as AND: [...] allows empty-condition detection to short-circuit sooner.

Prisma Client extension

createCaslExtension addresses this issue by letting Prisma Client handle CASL's empty conditions as empty query results instead of throwing forbidden error.

Important: make sure to add this extension to the Prisma Client instance you use for CASL-protected queries. Otherwise, some CASL restrictions may not be applied correctly.

Add it as any other regular extension:

import { PrismaClient } from '@prisma/client';
import { createCaslExtension } from '@casl/prisma';

const prisma = new PrismaClient().$extends(createCaslExtension());

With this extension, operations with an empty CASL condition return the same kind of "no records" result Prisma users usually expect:

const posts = await prisma.post.findMany({
  where: accessibleBy(ability).ofType('Post')
});
// posts === []

Single-record operations that require a matching record, such as findFirstOrThrow, findUniqueOrThrow, update, and delete, will reject with a "No record was found for an ..." error.

TypeScript support

The package is written in TypeScript what provides comprehensive IDE hints and compile time validation.

Makes sure to call prisma generate. @casl/prisma uses Prisma generated types, so if client is not generated nothing will work.

Additionally, there are several helpers that makes it easy to work with Prisma and CASL:

PrismaQuery

It's a generic type that provides Prisma.ModelWhereInput in generic way. We need to pass inside a named model:

import type { User } from '@prisma/client';
import type { ForcedSubject } from '@casl/ability';
import type { PrismaQuery } from '@casl/prisma';

// almost the same as Prisma.UserWhereInput except that it's a higher order type
type UserWhereInput = PrismaQuery<User & ForcedSubject<'User'>>;

Custom PrismaClient output path (Prisma 7 default)

Prisma 7's prisma-client generator writes the client into a custom directory, so @prisma/client no longer re-exports your project types. Point CASL at the generated client with a small wrapper built on @casl/prisma/runtime:

// schema.prisma
generator client {
  provider = "prisma-client"
  output   = "../src/generated/client"
}
// prisma.config.ts
import { defineConfig } from 'prisma/config';

export default defineConfig({
  schema: './schema.prisma',
  datasource: { url: process.env.DATABASE_URL! },
});
// src/casl-prisma.ts
import {
  accessibleBy,
  createPrismaAbility,
  ParsingQueryError,
  prismaQuery,
  type PrismaQueryOf,
  type WhereInputOf,
} from '@casl/prisma/runtime';
import type { Ability, ForcedSubject, TaggedSubject } from "@casl/ability";
import type { Prisma, Post, User } from './generated/client';

export { accessibleBy, createPrismaAbility, ParsingQueryError, prismaQuery };

export type PrismaQuery<T extends ForcedSubject<any> = ForcedSubject<any>> =
  PrismaQueryOf<Prisma.TypeMap, T>;
export type WhereInput<TModelName extends Prisma.ModelName> =
  WhereInputOf<Prisma.TypeMap, TModelName>;
export type AppSubjects =
  | User & ForcedSubject<'User'>
  | Post & ForcedSubject<'Post'>
;

export type AppAbility = Ability<['create' | "read" | "update" | "delete", 'all' | AppSubjects], PrismaQuery, TaggedSubject>

Use this wrapper in your app instead of importing from @casl/prisma directly. If you stay on the legacy prisma-client-js generator for Prisma 6.x compatibility, the default @casl/prisma entrypoint keeps working.

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing.

If you'd like to help us sustain our community and project, consider to become a financial contributor on Open Collective

See Support CASL for details

License

MIT License

版本与发布记录

@casl/prisma 共发布 30 个版本,首次发布于 2021/05/10;过去 12 个月发布了 9 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.2.0latest2026/10/2811 天前
2.1.02026/10/2811 天前
2.0.22026/07/074 个月前
2.0.12026/05/225 个月前
2.0.02026/05/215 个月前
2.0.0-rcnext2026/05/086 个月前

其他发布通道:alpha → 2.0.0-alpha.3,next → 2.0.0-rc。

关于 @casl/prisma 的常见问题

@casl/prisma 是什么?

Allows to query accessible records using Prisma client based on CASL rules

如何安装 @casl/prisma?

在项目目录运行 npm install @casl/prisma,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@casl/ability(^7.0.0)、@prisma/client(^4.16.0 || ^5.0.0 || ^6.0.0 || ^7.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@casl/prisma 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@casl/prisma 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@casl/prisma 可以商用吗?

@casl/prisma 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@casl/prisma 的下载量有多少?

根据 npm 官方下载统计,@casl/prisma 在 2026/10/09 – 2026/11/07 这 30 天内被下载 812,299 次,日均约 27,077 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@casl/prisma 还在维护吗?

最新版本 2.2.0 发布于 2026/10/28(11 天前);至今共发布 30 个版本,其中过去 12 个月发布了 9 个;首次发布于 2021/05/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。