{"id":16504,"library":"prisma-rls","title":"Prisma Row-Level Security Extension","description":"prisma-rls is a Prisma client extension designed to implement row-level security (RLS) on any database, including those without native RLS support (e.g., MySQL). It achieves this by automatically injecting 'where' clauses into all Prisma model queries, effectively filtering data based on defined permissions. The current stable version is `0.5.6`. The package shows active development with frequent minor releases and bug fixes, indicated by the recent changelog entries. A key differentiator is its database-agnostic approach, allowing RLS where it wouldn't natively exist, and its integration directly into the Prisma client query pipeline. It ships with TypeScript types, providing a type-safe way to define permissions configurations and contexts. It's crucial to note that this extension does not apply to raw database queries, which require manual handling.","status":"active","version":"0.5.6","language":"javascript","source_language":"en","source_url":"https://github.com/s1owjke/prisma-rls","tags":["javascript","database","prisma","prisma-client","prisma-extension","rls","row-level-security","typescript"],"install":[{"cmd":"npm install prisma-rls","lang":"bash","label":"npm"},{"cmd":"yarn add prisma-rls","lang":"bash","label":"yarn"},{"cmd":"pnpm add prisma-rls","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required Prisma Client for extension functionality and type generation.","package":"@prisma/client","optional":false}],"imports":[{"note":"Primary entry point for creating the RLS extension; `require()` is not supported for this ESM-first library.","wrong":"const { createRlsExtension } = require('prisma-rls');","symbol":"createRlsExtension","correct":"import { createRlsExtension } from 'prisma-rls';"},{"note":"This is a runtime value, not just a type, as it defines the structure for permissions at runtime.","wrong":"import type { PermissionsConfig } from 'prisma-rls';","symbol":"PermissionsConfig","correct":"import { PermissionsConfig } from 'prisma-rls';"},{"note":"This is a TypeScript type, so use `import type` for clarity and to avoid bundling issues.","wrong":"import { ExtensionOptions } from 'prisma-rls';","symbol":"ExtensionOptions","correct":"import type { ExtensionOptions } from 'prisma-rls';"}],"quickstart":{"code":"import { Prisma, PrismaClient } from \"@prisma/client\";\nimport Fastify, { FastifyRequest } from \"fastify\";\nimport { createRlsExtension, PermissionsConfig } from \"prisma-rls\";\n\n// Define shared types for roles and permissions context\nexport type Role = \"User\" | \"Guest\";\nexport type PermissionsContext = { userId: string | null };\nexport type RolePermissions = PermissionsConfig<Prisma.TypeMap, PermissionsContext>;\nexport type PermissionsRegistry = Record<Role, RolePermissions>;\n\n// Define user permissions\nconst userPermissions: RolePermissions = {\n  Post: {\n    read: { published: { equals: true } },\n    create: true,\n    update: (ctx) => ({ authorId: { equals: ctx.userId } }),\n    delete: (ctx) => ({ authorId: { equals: ctx.userId } })\n  },\n  User: {\n    read: (ctx) => ({ id: { equals: ctx.userId } }),\n    create: false,\n    update: (ctx) => ({ id: { equals: ctx.userId } }),\n    delete: false\n  }\n};\n\n// Define guest permissions\nconst guestPermissions: RolePermissions = {\n  Post: {\n    read: { published: { equals: true } },\n    create: false,\n    update: false,\n    delete: false\n  },\n  User: {\n    read: false,\n    create: false,\n    update: false,\n    delete: false\n  }\n};\n\n// Combine permissions into a registry\nexport const permissionsRegistry = {\n  User: userPermissions,\n  Guest: guestPermissions\n} satisfies PermissionsRegistry;\n\n(async () => {\n  const prisma = new PrismaClient();\n  const server = Fastify();\n\n  // Dummy function to resolve user from auth header\n  const resolveUser = async (authorizationHeader?: string | string[] | undefined) => {\n    if (authorizationHeader === 'Bearer user-token') {\n      return { id: 'user-123', role: 'User' };\n    }\n    return null;\n  };\n\n  server.decorateRequest('db', null);\n\n  server.addHook('onRequest', async (request: any, reply) => {\n    const user = await resolveUser(request.headers.authorization);\n    const userRole: Role = user ? user.role : \"Guest\";\n    const permissionsContext: PermissionsContext = { userId: user?.id ?? null };\n\n    const rlsExtension = createRlsExtension({\n      dmmf: Prisma.dmmf,\n      permissionsConfig: permissionsRegistry[userRole],\n      context: permissionsContext,\n    });\n    request.db = prisma.$extends(rlsExtension);\n  });\n\n  server.get(\"/posts\", async function handler(request: any, reply) {\n    // Assuming a user with 'user-token' can only see their own posts\n    // and public posts. Guests can only see public posts.\n    return await request.db.post.findMany();\n  });\n\n  server.get(\"/profile\", async function handler(request: any, reply) {\n    // A user can only see their own profile, guests see nothing.\n    return await request.db.user.findMany(); // Will apply RLS based on `userId`\n  });\n\n  await server.listen({ port: 8080, host: \"0.0.0.0\" });\n  console.log('Server listening on http://0.0.0.0:8080');\n})();","lang":"typescript","description":"This example demonstrates how to integrate `prisma-rls` with a Fastify server to apply row-level security based on user roles and context. It defines permissions for 'User' and 'Guest' roles, creates a dynamic Prisma client extension per request, and applies RLS to `Post` and `User` model queries. Authorization is simulated via a bearer token."},"warnings":[{"fix":"Review and update error handling for `AuthorizationError` and `ReferentialIntegrityError` classes. Ensure types are correctly inferred with the narrower type definitions.","message":"Version `0.5.0` introduced major internal type safety improvements and refactored error handling to use dedicated error classes (`AuthorizationError`, `ReferentialIntegrityError`). Existing error handling logic might need updates.","severity":"breaking","affected_versions":">=0.5.0"},{"fix":"Avoid using raw queries for operations requiring RLS, or implement custom security checks and `WHERE` clauses for all raw queries.","message":"The `prisma-rls` extension does not apply to raw Prisma queries (e.g., `prisma.$queryRaw`, `prisma.$executeRaw`). RLS for raw queries must be handled manually at the database level or through application-side validation.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Enable the `checkRequiredBelongsTo` flag in `createRlsExtension` options if explicit checks are required for these relations, accepting potential performance implications. Alternatively, design your schema to avoid mandatory 'belongs-to' relations where RLS is critical, or handle these specific cases with custom logic.","message":"When dealing with mandatory 'belongs-to' relations (where the foreign key owner side is required), Prisma typically does not generate filters to prevent referential integrity violations. This can lead to unexpected behavior where RLS might not apply as expected.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure that your `PermissionsContext` object is always fully populated with expected values, or add null-checking/optional chaining within your permission functions, for example: `permissionContext.user?.id`.","cause":"The `PermissionsContext` object or a property within it (e.g., `user` or `user.id`) is `null` or `undefined` when a permission function tries to access it.","error":"TypeError: Cannot read properties of undefined (reading 'id')"},{"fix":"Review the permission definition for the specific model and operation. Ensure the permission function returns a valid `where` object or `true` for allowed access, or `false` for explicit denial, rather than an implicitly empty object.","cause":"A permission function evaluated to an empty `where` object when a non-empty `where` clause was expected, potentially due to a context issue or a misconfigured permission that disallows access entirely (e.g., `false`).","error":"PrismaClientKnownRequestError: Argument 'where' must not be empty."}],"ecosystem":"npm"}