# Comparison URL: https://permix.letstri.dev/docs/comparison Comparison with other libraries *** title: Comparison description: Comparison with other libraries icon: RiArrowLeftRightFill -------------------------- ## Overview Permix is a library that provides a way to manage permissions in your application. It is designed to be used with React, Vue, etc. But not only Permix can manage permissions, there are other libraries that can do the same thing. ## Comparison | Feature | Permix | CASL | | ------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | Type-safe | ✅ Native | ✅ Via [external type](https://casl.js.org/v6/en/advanced/typescript#permissions-inference) | | Saving rules | ✅ Via [template](/docs/guide/template) | ✅ Via [AppAbility](https://casl.js.org/v6/en/cookbook/cache-rules#the-issue) | | Hydration | ✅ Native | ⚠️ Via custom implementation | | Entity | ✅ Depends on props of a [passed object](/docs/guide/setup#type-based) | ⚠️ Class-based yes, but object-based via [external function](https://casl.js.org/v6/en/guide/subject-type-detection) | | Events | ✅ | ❌ | | Simple DX | ✅ Create instance, use built-in integrations | ❌ In CASL you need to manage a lot of stuff manually (type-safe, hydration, etc.) | | Modernity | ✅ Uses modern updates and features of each lib and framework | ❌ CASL was created a long time ago and hasn't updated the core | | Size | \~2.5kb | \~21kb (ability) + \~2.5kb (framework adapter) | # Introduction URL: https://permix.letstri.dev/docs The type-safe permission management you've always needed *** title: Introduction description: The type-safe permission management you've always needed icon: RiInboxLine ----------------- ## Idea In my many years of experience, I have worked extensively with permissions management, and early in my career I wrote solutions that looked like this: ```ts if (user.role === 'admin') { // do something } ``` Later, I started using [CASL](https://casl.js.org) for permissions management in a [Vue](https://vuejs.org/) application. ```ts can('read', ['Post', 'Comment']); can('manage', 'Post', { author: 'me' }); can('create', 'Comment'); ``` But time goes on, CASL becomes older, and developers' needs grow, especially for type-safe libraries. Unfortunately, CASL couldn't satisfy my type validation needs and so I started thinking again about writing my own validation solution. But this time I wanted to make it as a library, as I already had experience with open-source. ## Implementation I started to create my own solution. However, nothing occurred to me until I watched a Web Dev Simplified [video](https://www.youtube.com/watch?v=5GG-VUvruzE) where he demonstrated an example of implementing permission management as he envisioned it. I really liked his approach because it was based on type-safety, which is exactly what I needed. So I'm ready to present to you my permission management solution called Permix! ## DX When creating Permix, the goal was to simplify DX as much as possible without losing type-safety and provide the necessary functionality. That is why you only need to write the following code to get started: ```ts twoslash import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'read' } }>() permix.setup({ post: { read: true, } }) const canReadPost = permix.check('post', 'read') // true ``` It looks too simple, so here's a more interesting example: ```ts twoslash import type { PermixDefinition } from 'permix' import { createPermix } from 'permix' // You can take types from your database interface User { id: string role: 'editor' | 'user' } interface Post { id: string title: string authorId: string published: boolean } interface Comment { id: string content: string authorId: string } // Create definition to describe your permissions type PermissionsDefinition = PermixDefinition<{ post: { dataType: Post action: 'create' | 'read' | 'update' | 'delete' } comment: { dataType: Comment action: 'create' | 'read' | 'update' } }> const permix = createPermix() // Define permissions for different users const editorPermissions = permix.template({ post: { create: true, read: true, update: post => !post?.published, delete: post => !post?.published, }, comment: { create: false, read: true, update: false, }, }) const userPermissions = permix.template(({ id: userId }: User) => ({ post: { create: false, read: true, update: false, delete: false, }, comment: { create: true, read: true, update: comment => comment?.authorId === userId, }, })) async function getUser() { // Imagine that this function is fetching user from database return { id: '1', role: 'editor' as const, } } // Setup permissions for signed in user async function setupPermix() { const user = await getUser() const permissionsMap = { editor: () => editorPermissions(), user: () => userPermissions(user), } permix.setup(permissionsMap[user.role]()) } // Call setupPermix where you need to setup permissions setupPermix() // Check if a user has permission to do something const canCreatePost = permix.check('post', 'create') async function getComment() { // Imagine that this function is fetching comment from database return { id: '1', content: 'Hello, world!', authorId: '1', } } const comment = await getComment() const canUpdateComment = permix.check('comment', 'update', comment) ``` ## Benefits What are the benefits of using Permix? * 100% type-safe without writing TypeScript (except for initialization) * Single source of truth for your entire app * Perfect match for TypeScript monorepos * Zero dependencies * Useful methods for specific cases * Large number of integrations for different frameworks, such as [React](/docs/integrations/react), [Vue](/docs/integrations/vue), [Express](/docs/integrations/express), and more. ## Ready? Ready to take Permix to your project? Let's go to the [Quick Start](/docs/quick-start) page. # Quick Start URL: https://permix.letstri.dev/docs/quick-start A quick start guide to start using Permix and validating your permissions *** title: Quick Start description: A quick start guide to start using Permix and validating your permissions icon: RiPlayLargeLine --------------------- ## Try Permix Want to explore Permix before installing? Try our interactive sandbox environment where you can experiment with type-safe permissions management right in your browser. [Try Permix Sandbox](https://stackblitz.com/edit/permix-sandbox?file=src%2Fmain.ts\&terminal=dev) ## Installation ### Install Permix Typically you'll need to install Permix using your package manager: ```package-install permix ``` ### Create an instance To create a base instance, you need to provide a schema as a generic type to `createPermix` function that defines your permissions: ```ts title="/lib/permix.ts" import { createPermix } from 'permix' export const permix = createPermix<{ post: { action: 'create' | 'read' | 'update' | 'delete' } }>() // ... ``` Learn more about features and configuration of instances in the [instance guide](/docs/guide/instance). ### Setup your permissions You can setup your permissions by calling `setup` method on your instance in any place you want: ```ts title="/lib/permix.ts" // ... // Call setupPermissions in your application export function setupPermissions() { permix.setup({ post: { create: true, read: true, update: true, delete: false, }, }) } ``` ### Check permissions After setup, you can use `check` method to check available permissions: ```ts permix.check('post', 'create') // true ``` ### Finish That's it! 🎉 You've now got a basic setup of Permix. The next step is to learn more about 3 core concepts of Permix: * [Instance](/docs/guide/instance) * [Setup](/docs/guide/setup) * [Check](/docs/guide/check) * Of course, do not hesitate to read other guide pages. ## Integrations Continuing from the quick start, you can now explore how Permix integrates with other libraries and frameworks. Integration with React via provider and hook. Integration with Vue via plugin and composable. Integration with Node.js via middleware. Integration with Hono via middleware. Integration with Express via middleware. Integration with tRPC via middleware. # Check URL: https://permix.letstri.dev/docs/guide/check Learn how to check permissions in your application *** title: Check description: Learn how to check permissions in your application --------------------------------------------------------------- ## Overview Permix provides two methods for checking permissions: `check` and `checkAsync`. Both methods return a boolean indicating whether the action is allowed. ## `check` The `check` method allows you to verify if certain actions are permitted. It returns a boolean indicating whether the action is allowed: ```ts permix.check('post', 'create') // returns true/false ``` ## Array You can check multiple actions at once by passing an array of actions. All actions must be permitted for the check to return true: ```ts // Check if both create and read are allowed permix.check('post', ['create', 'read']) // returns true if both are allowed ``` ## All Use the special 'all' keyword to verify if all possible actions for an entity are permitted: ```ts // Check if all actions are allowed for posts permix.check('post', 'all') // returns true only if all actions are permitted ``` ## `checkAsync` When you need to ensure permissions are ready before checking, use `checkAsync`. This is useful when permissions might be set up asynchronously: ```ts setTimeout(() => { permix.setup({ post: { create: true } }) }, 1000) await permix.checkAsync('post', 'create') // waits for setup ``` In most cases you should use `check` instead of `checkAsync`. `checkAsync` is useful when you need to ensure permissions are ready before checking, for example in route middleware. ## Data-Based You can define permissions that depend on the data being accessed: ```ts permix.setup({ post: { // Only allow updates if user is the author update: post => post.authorId === currentUserId, // Static permission read: true } }) // Check with data const post = { id: '1', authorId: 'user1' } permix.check('post', 'update', post) // true if currentUserId === 'user1' ``` You still can check permissions without providing the data, but it will return `false` in this case. ## Type Safety Permix provides full type safety for your permissions: ```ts twoslash import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'update' } }>() // @errors: 2345 // This will cause a TypeScript error but will return false permix.check('post', 'invalid-action') permix.check('invalid-entity', 'create') ``` # Events URL: https://permix.letstri.dev/docs/guide/events Learn how to handle permission updates in your application *** title: Events description: Learn how to handle permission updates in your application ----------------------------------------------------------------------- ## Overview Permix provides an event system that allows you to react to permission changes in your application. Each event provides type-safe data and hooks to register handlers. ## Usage You can register event handlers using the `hook` and `hookOnce` methods: ```ts const permix = createPermix<{ post: { action: 'create' | 'read' } }>() // The handler will be called every time setup is executed permix.hook('setup', () => { console.log('Permissions were updated') }) // The handler will be called only once permix.hookOnce('setup', () => { console.log('Permissions were updated once') }) // Calling `setup` method triggers the `setup` event // and `ready` event if called on the client side permix.setup({ post: { create: true, read: true } }) ``` Available events: * `setup` - Triggered when permissions are updated through the `setup` method. * `ready` - Triggered when the permissions are ready to be used. * `hydrate` - Triggered when the permissions are hydrated from the server. # Hydration (SSR) URL: https://permix.letstri.dev/docs/guide/hydration Learn how to hydrate and dehydrate permissions in your application *** title: Hydration (SSR) description: Learn how to hydrate and dehydrate permissions in your application ------------------------------------------------------------------------------- ## Overview Hydration is the process of converting server-side state into client-side state. In Permix, hydration allows you to serialize permissions on the server and restore them partially on the client. Note that function-based permissions will be converted to `false` during dehydration since functions cannot be serialized to JSON. That's why you should call `setup` method on the client side immediately after hydration. ## Usage Permix provides two methods for handling hydration: * `dehydrate` - Converts the current permissions state into a JSON-serializable format * `hydrate` - Restores permissions from a previously dehydrated state ```ts twoslash import { createPermix, dehydrate, hydrate } from 'permix' const permix = createPermix<{ post: { dataType: { isPublic: boolean } action: 'create' | 'read' } }>() // Set up initial permissions permix.setup({ post: { create: true, read: post => !!post?.isPublic } }) // Dehydrate permissions to JSON const state = dehydrate(permix) // Result: { post: { create: true, read: false } } // Later, hydrate permissions from the state hydrate(permix, state) ``` ## Server-Side Rendering Hydration is particularly useful in server-side rendering scenarios where you want to transfer permissions from the server to the client: ```ts twoslash // Express server import express from 'express' import { createPermix, dehydrate } from 'permix' const app = express() const permix = createPermix<{ post: { action: 'create' | 'read' } }>() app.get('/', (req, res) => { // Setup permissions on the server permix.setup({ post: { create: true, read: true } }) // Dehydrate permissions for client const dehydratedState = dehydrate(permix) // Send HTML with embedded permissions data res.send(`
`) }) ``` # Instance URL: https://permix.letstri.dev/docs/guide/instance Learn how to create a new Permix instance *** title: Instance description: Learn how to create a new Permix instance ------------------------------------------------------ ## Overview Instance is the main entry point for Permix that will check permissions in every returned method. To create an instance, you need to use the `createPermix` function. ## TypeScript Permix is built with TypeScript, providing type safety and validation. Using TypeScript enables autocompletion and compile-time checks for your permission definitions. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'edit' } }>() ``` ### Generic type Permix instance accepts a generic type to define permissions. #### `action` Union type of all actions you want to check on the entity. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'edit' // ^^^^^^ } }>() ``` #### `dataType` Not required, but recommended. To define a type of your entities, you can pass the `dataType` property to a generic type. This is useful if you want to check permissions on a specific entity otherwise the type will be `unknown`. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' interface Post { id: string author: string content: string } const permix = createPermix<{ post: { dataType: Post // ^^^^^^^^ action: 'create' | 'edit' } }>() permix.setup({ post: { create: true, edit: post => post?.author === 'John Doe' // ^? } }) const somePost: Post = { id: '1', author: 'John Doe', content: 'Hello World' } const canEdit = permix.check('post', 'edit') // false const canEditWithPost = permix.check('post', 'edit', somePost) // true ``` #### `dataRequired` Not required, defaults to `false`. By default, when you define a `dataType`, the data parameter in permission checks is optional. You can make it required by setting `dataRequired: true`. This ensures that permission checks for that entity must always include the data parameter. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' interface Post { id: string author: string content: string } const permix = createPermix<{ post: { dataType: Post dataRequired: true // ^^^^^^^^^^^^ action: 'create' | 'edit' } }>() permix.setup({ post: { create: true, edit: post => post.author === 'John Doe' // ^? } }) const somePost: Post = { id: '1', author: 'John Doe', content: 'Hello World' } // @errors: 2554 const canCreate = permix.check('post', 'create') const canEdit = permix.check('post', 'edit', somePost) // ✅ Valid ``` When `dataRequired` is `true`, TypeScript will enforce that you must pass the data parameter when checking permissions for that entity. This is useful when your permission logic always depends on the entity data and you want to prevent accidental calls without the required data. #### `PermixDefinition` You can use `PermixDefinition` type to define your permissions separately from the instance. ```ts twoslash title="/lib/permix.ts" import type { PermixDefinition, PermixRules } from 'permix' import { createPermix } from 'permix' type PermissionsDefinition = PermixDefinition<{ post: { action: 'create' | 'edit' } }> async function getRules(): Promise> { // get user or something like that return { post: { create: true, edit: false } } } const permix = createPermix() permix.setup(await getRules()) ``` ### Return type Each Permix instance provides a list of methods to manage and check permissions. These methods are documented in detail on their separate pages. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' const permix = createPermix() // @noErrors permix. // ^| ``` ## JavaScript Not using TypeScript? Permix works perfectly fine even with plain JavaScript. ```ts title="/lib/permix.js" const permix = createPermix() ``` ## Initial Rules You can provide initial rules when creating a Permix instance. This allows you to set up permissions immediately without calling `setup` separately. ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'edit' } }>({ post: { create: true, edit: false } }) // Permissions are immediately available console.log(permix.check('post', 'create')) // true console.log(permix.isReady()) // true ``` This is equivalent to: ```ts twoslash title="/lib/permix.ts" import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'edit' } }>() permix.setup({ post: { create: true, edit: false } }) ``` Initial rules are useful when you have permissions that are known at initialization time and don't need to be loaded asynchronously. You still should pass generic type to `createPermix` even if you provide initial rules. Otherwise, Permix will not be able to validate your permissions. # Ready State URL: https://permix.letstri.dev/docs/guide/ready Learn how to use the `isReady()` method to check if permissions are ready to use. *** title: Ready State description: Learn how to use the `isReady()` method to check if permissions are ready to use. ---------------------------------------------------------------------------------------------- ## Overview Sometimes you need to know when permissions are ready to use. For example, you might want to wait for permissions to be ready before rendering a component. That's where the `isReady()` and `isReadyAsync()` methods come in. Note that `isReady()` and `isReadyAsync()` will always return `false` on server-side. It only becomes `true` after the first successful call to `setup()` on the client. ## Usage ### Basic Permix provides an `isReady()` method to check if permissions have been properly initialized on the client side: ```ts twoslash import { createPermix } from 'permix' const permix = createPermix() console.log(permix.isReady()) // false // After setup completes permix.setup({ post: { create: true, read: true } }) console.log(permix.isReady()) // true ``` ### Async If you need to wait for permissions to be ready in an async context, you can use the `isReadyAsync()` method. This returns a promise that resolves when permissions are ready: ```ts import { createPermix } from 'permix' const permix = createPermix() async function init() { await permix.isReadyAsync() // Permissions are now ready to use const canCreate = permix.check('post', 'create') } ``` ### SSR This is particularly useful in SSR applications when using function-based permissions, since the dehydration process converts all function permissions to `false` until they are properly rehydrated on the client. Read more about [hydration](/guide/hydration) to learn how to transfer permissions from the server to the client. ```ts import { dehydrate, hydrate, createPermix } from 'permix' import { permix } from './permix' permix.setup({ post: { create: true, read: post => post.isPublic } }) // Dehydrate permissions on the server const state = dehydrate(permix) // { post: { create: true, read: false } } // Rehydrate permissions on the client hydrate(permix, state) const canRead = permix.check('post', 'read', { isPublic: true }) // false permix.hook('ready', (state) => { const canRead = permix.check('post', 'read', { isPublic: true }) console.log(canRead) // true }) ``` # Setup URL: https://permix.letstri.dev/docs/guide/setup Learn how to setup permissions in your project *** title: Setup description: Learn how to setup permissions in your project ----------------------------------------------------------- ## Overview After creating Permix instance, you need to define permissions with `setup` method. You can call `setup` in any time with any permissions and Permix will replace the previous permissions. You always should describe all permissions in the `setup` method that was defined in the Permix generic type. For role separation, you can use the [`template`](/docs/guide/template) method. ### Object definition ```ts const permix = createPermix<{ post: { action: 'create' } comment: { action: 'create' | 'update' } }>() permix.setup({ post: { create: true, }, comment: { create: true, update: true, } }) ``` You can also use `enum` based permissions. See [Enum-based](https://github.com/letstri/permix/tree/main/examples/enum-based) for more information. ## Initial You can set up initial rules directly when creating a Permix instance by passing them as the first parameter to `createPermix`. ```ts twoslash import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'read' } comment: { action: 'create' | 'read' | 'update' } }>({ post: { create: true, read: true, }, comment: { create: false, read: true, update: true, }, }) // The instance is immediately ready to use permix.check('post', 'create') // true permix.isReady() // true ``` When using initial rules, the Permix instance is immediately ready to use without calling `setup` first. ## Type-Based When creating a Permix instance, you can define entities that will be used in the `setup` method for each related entity. This allows you to check permissions for specific data entities. So instead of `boolean` you can use functions to check permissions. ```ts twoslash import { createPermix } from 'permix' interface Post { id: string authorId: string } const permix = createPermix<{ post: { dataType: Post // ^^^^^^^^ action: 'update' } comment: { action: 'update' } }>() // @noErrors permix.setup({ post: { update: post => post. // ^| } comment: { update: comment => c // ^? } }) ``` ### Required By default `dataType` providing optional type to callbacks but you can make it required by adding `dataRequired: true`. ```ts twoslash import { createPermix } from 'permix' interface Post { id: string authorId: string } interface Comment { id: string postId: string } const permix = createPermix<{ post: { dataType: Post dataRequired: true action: 'update' } comment: { dataType: Comment action: 'update' } }>() // @noErrors permix.setup({ post: { update: post => !!post.authorId // ^? } comment: { update: comment => !!comment.postId // ^? } }) ``` ### unknown If you cannot define entity types in the Permix instance, the types will be `unknown` by default, but you can still define them later in the `setup` method. This approach is not recommended as it reduces type safety and IDE support. ```ts twoslash import { createPermix } from 'permix' const permix = createPermix<{ comment: { action: 'update' } }>() // @noErrors permix.setup({ comment: { update: (comment: { authorId: string }) => comment.authorId === user.id } }) ``` ## Dynamic You can use async functions to fetch permissions from external sources and then set them up. See the [template](/docs/guide/template) for more examples and patterns. ```ts import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' | 'read' | 'update' | 'delete' } comment: { action: 'create' | 'read' | 'update' | 'delete' } }>() // Fetch permissions from API async function loadUserPermissions(userId: string) { const permissions = await getPermissionsFromAnyPlace() permix.setup({ post: { create: permissions.includes('post:create'), read: permissions.includes('post:read'), update: permissions.includes('post:update'), delete: permissions.includes('post:delete'), }, comment: { create: permissions.includes('comment:create'), read: permissions.includes('comment:read'), update: permissions.includes('comment:update'), delete: permissions.includes('comment:delete'), }, }) } // Usage await loadUserPermissions('user-123') ``` ## Getting Rules You can get the current rules from an existing Permix instance using the `getRules` function: ```ts import { getRules } from 'permix' // Get current rules const rules = getRules(permix) ``` The `getRules` function returns the exact rules object that was set using `setup`, including any permission functions. # Template URL: https://permix.letstri.dev/docs/guide/template Learn how to define permissions using templates *** title: Template description: Learn how to define permissions using templates ------------------------------------------------------------ ## Overview Permix provides a `template` method that allows you to define permissions in a separate location from where they are set up. This is useful for organizing permission definitions and reusing them across different parts of your application. Templates are validated when they are created, ensuring your permission definitions are correct before runtime. ## Basic Usage The simplest way to use templates is to define static permissions: ```ts const adminPermissions = permix.template({ post: { create: true, read: true } }) // Later, use the template to setup permissions permix.setup(adminPermissions()) ``` ## Dynamic Templates Templates can accept parameters to create dynamic permissions based on runtime values: ```ts interface User { id: string role: string } const userPermissions = permix.template(({ id: userId }: User) => ({ post: { create: true, read: true, update: post => post?.authorId === userId } })) // Use with specific user data const user = await getUser() permix.setup(userPermissions(user)) ``` ## Type Safety Templates maintain full type safety from your Permix instance definition: ```ts twoslash import { createPermix } from 'permix' const permix = createPermix<{ post: { action: 'create' } }>() // @errors: 2353 // This will throw an error const invalidTemplate = permix.template({ post: { edit: true } }) ``` ## Role-Based Example Templates are particularly useful for role-based permission systems: ```ts const editorPermissions = permix.template({ post: { create: true, read: true, update: post => !post?.published, delete: post => !post?.published } }) const userPermissions = permix.template(({ id: userId }: User) => ({ post: { create: false, read: true, update: post => post?.authorId === userId, delete: false } })) // Setup based on user role function setupPermissions() { const user = await getUser() const permissionsMap = { editor: () => editorPermissions(), user: () => userPermissions(user) } return permix.setup(permissionsMap[user.role]()) } ``` ## Standalone Templates You can define permission templates outside of the Permix instance using the `PermixRules` type. This is useful when you want to organize your permission logic in separate files: ```ts twoslash import type { PermixRules, PermixDefinition } from 'permix' import { createPermix } from 'permix' // Define your Permix definition type type Definition = PermixDefinition<{ post: { dataType: { id: string; authorId: string } action: 'create' | 'read' | 'update' | 'delete' } }> // It can be in separate file and imported here const permix = createPermix() // Create a standalone template function function userPermissions(userId: string, role: 'admin' | 'user'): PermixRules { return { post: { create: role === 'admin', read: true, update: role === 'admin' ? true : (post) => post?.authorId === userId, delete: role === 'admin' } } } // Later, use it with your Permix instance const permissions = userPermissions('1', 'admin') permix.setup(permissions) ``` This approach allows you to: * Keep permission logic separate from your Permix instance * Reuse permission templates across different parts of your application * Maintain full type safety with your Permix definition # Elysia URL: https://permix.letstri.dev/docs/integrations/elysia Learn how to use Permix with Elysia *** title: Elysia description: Learn how to use Permix with Elysia ------------------------------------------------ ## Overview Permix provides integration for Elysia that allows you to easily check permissions in your routes. The integration can be created using the `createPermix` function. Before getting started with Elysia integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with Elysia: ```ts twoslash import { Elysia } from 'elysia' import { createPermix } from 'permix/elysia' interface Post { id: string authorId: string title: string content: string } // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' | 'delete' } }>() // Initialize Elysia const app = new Elysia() // Derive your permission rules .derive(({ headers }) => { // You can access body or other properties to determine permissions const isAuthorized = !!headers.authorization?.slice(7) return permix.derive({ post: { create: true, read: true, update: isAuthorized, delete: isAuthorized } }) }) ``` The derive preserves full type safety from your Permix definition, ensuring your permission checks are type-safe. ## Checking Permissions Use the `checkHandler` function in your Elysia routes to check permissions: ```ts app.post('/posts', () => { // Create post logic here return { success: true } }, { beforeHandle: permix.checkHandler('post', 'create') }) // Check multiple actions app.put('/posts/:id', () => { // Update post logic here return { success: true } }, { beforeHandle: permix.checkHandler('post', ['read', 'update']) }) // Check all actions app.delete('/posts/:id', () => { // Delete post logic here return { success: true } }, { beforeHandle: permix.checkHandler('post', 'all') }) ``` ## Accessing Permix Directly You can access the Permix instance directly in your route handlers using the context: ```ts app.get('/posts', ({ permix }) => { // Check permissions manually if (permix.check('post', 'read')) { // User has permission to read posts return { posts: getAllPosts() } } else { return { error: 'You do not have permission to read posts' } } }) ``` The `get` function returns the Permix instance with available methods. ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true } }) // Create a template for regular user permissions const userTemplate = permix.template({ post: { create: true, read: true, update: false, delete: false } }) // Use templates in your middleware app.derive(({ headers }) => { const user = await getUserFromDb(headers.authorization.slice(7)) return permix.derive( user?.role === 'admin' ? adminTemplate : userTemplate ) }) ``` ## Custom Error Handling By default, the middleware returns a 403 Forbidden response. You can customize this behavior by providing an `onForbidden` handler: ### Basic Error Handler ```ts const permix = createPermix({ onForbidden: ({ context }) => { context.set.status = 403 return { error: 'Custom forbidden message' } } }) ``` ### Dynamic Error Handler You can also provide a handler that returns different responses based on the entity and actions: ```ts const permix = createPermix({ onForbidden: ({ context, entity, actions }) => { context.set.status = 403 if (entity === 'post' && actions.includes('create')) { return { error: `You don't have permission to ${actions.join('/')} a ${entity}` } } return { error: 'You do not have permission to perform this action' } } }) ``` The `onForbidden` handler receives: * `context`: Elysia Context object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts app.derive(async ({ headers }) => { // Fetch user permissions from database const userId = headers.authorization?.slice(7) const userPermissions = await getUserPermissions(userId) return permix.derive({ post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } }) }) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts app.put('/posts/:id', async ({ params, permix }) => { const postId = params.id const post = await getPostById(postId) // Check if user can update this specific post if (permix.check('post', 'update', post)) { // Update post logic return { success: true } } else { return { error: 'You cannot update this post' } } }) ``` # Express URL: https://permix.letstri.dev/docs/integrations/express Learn how to use Permix with Express *** title: Express description: Learn how to use Permix with Express ------------------------------------------------- ## Overview Permix provides middleware for Express that allows you to easily check permissions in your routes. The middleware can be created using the `createPermix` function. Before getting started with Express integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with Express: ```ts twoslash import express from 'express' import { createPermix } from 'permix/express' interface Post { id: string authorId: string title: string content: string } // Initialize Express const app = express() // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' } }>() // Set up the middleware with your permission rules app.use(permix.setupMiddleware(({ req }) => { // You can access req.user or other properties to determine permissions return { post: { create: true, read: true, update: false } } })) ``` The middleware preserves full type safety from your Permix definition, ensuring your permission checks are type-safe. ## Checking Permissions Use the `checkMiddleware` function in your Express routes to check permissions: ```ts app.post('/posts', permix.checkMiddleware('post', 'create'), (req, res) => { res.json({ success: true }) }) // Check multiple actions app.put('/posts/:id', permix.checkMiddleware('post', ['read', 'update']), (req, res) => { res.json({ success: true }) }) // Check all actions app.delete('/posts/:id', permix.checkMiddleware('post', 'all'), (req, res) => { res.json({ success: true }) }) ``` ## Accessing Permix Directly You can access the Permix instance directly in your route handlers using the `get` function: ```ts app.get('/posts', (req, res) => { const { check } = permix.get(req, res) // Check permissions manually if (check('post', 'read')) { // User has permission to read posts res.json({ posts: getAllPosts() }) } else { res.status(403).json({ error: 'You do not have permission to read posts' }) } }) ``` The `get` function returns the Permix instance with available methods. ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true } }) // Use the template in your middleware app.use(permix.setupMiddleware(({ req }) => { // You can still customize the template based on request data if (req.user?.role === 'admin') { return adminTemplate } return { post: { create: false, read: true, update: false } } })) ``` ## Custom Error Handling By default, the middleware returns a 403 Forbidden response. You can customize this behavior by providing an `onForbidden` handler: ### Basic Error Handler ```ts const permix = createPermix({ onForbidden: ({ res }) => { res.status(403).json({ error: 'Custom forbidden message', }) } }) ``` ### Dynamic Error Handler You can also provide a handler that returns different responses based on the entity and actions: ```ts const permix = createPermix({ onForbidden: ({ res, entity, actions }) => { if (entity === 'post' && actions.includes('create')) { res.status(403).json({ error: `You don't have permission to ${actions.join('/')} a ${entity}`, }) return } res.status(403).json({ error: 'You do not have permission to perform this action', }) } }) ``` The `onForbidden` handler receives: * `req`: Express Request object * `res`: Express Response object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts app.use(permix.setupMiddleware(async ({ req }) => { // Fetch user permissions from database const userPermissions = await getUserPermissions(req.user.id) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts } } })) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts app.put('/posts/:id', async (req, res, next) => { const post = await getPostById(req.params.id) const { check } = permix.get(req, res) // Check if user can update this specific post if (check('post', 'update', post)) { next() } else { res.status(403).json({ error: 'You cannot update this post' }) } }) ``` # Fastify URL: https://permix.letstri.dev/docs/integrations/fastify Learn how to use Permix with Fastify *** title: Fastify description: Learn how to use Permix with Fastify ------------------------------------------------- ## Overview Permix provides a plugin for Fastify that allows you to easily check permissions in your routes. The plugin can be created using the `createPermix` function. Before getting started with Fastify integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix plugin with Fastify: ```ts twoslash import Fastify from 'fastify' import { createPermix } from 'permix/fastify' interface Post { id: string authorId: string title: string content: string } // Initialize Fastify const fastify = Fastify() // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' } }>() // Set up the plugin with your permission rules fastify.register(permix.plugin(({ request, reply }) => { // You can access request.user or other properties to determine permissions return { post: { create: true, read: true, update: false } } })) ``` The plugin preserves full type safety from your Permix definition, ensuring your permission checks are type-safe. ## Checking Permissions Use the `checkHandler` function in your Fastify routes to check permissions: ```ts fastify.post('/posts', { preHandler: permix.checkHandler('post', 'create'), }, (request, reply) => { reply.send({ success: true }) }) // Check multiple actions fastify.put('/posts/:id', { preHandler: permix.checkHandler('post', ['read', 'update']), }, (request, reply) => { reply.send({ success: true }) }) // Check all actions fastify.delete('/posts/:id', { preHandler: permix.checkHandler('post', 'all'), }, (request, reply) => { reply.send({ success: true }) }) ``` ## Accessing Permix Directly You can access the Permix instance directly in your route handlers using the `get` function: ```ts fastify.get('/posts', (request, reply) => { const { check } = permix.get(request, reply) // Check permissions manually if (check('post', 'read')) { // User has permission to read posts reply.send({ posts: getAllPosts() }) } else { reply.status(403).send({ error: 'You do not have permission to read posts' }) } }) ``` The `get` function returns the Permix instance with available methods. ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true } }) // Use the template in your middleware fastify.register(permix.plugin(({ request }) => { // You can still customize the template based on request data if (request.user?.role === 'admin') { return adminTemplate } return { post: { create: false, read: true, update: false } } })) ``` ## Custom Error Handling By default, the plugin returns a 403 Forbidden response. You can customize this behavior by providing an `onForbidden` handler: ### Basic Error Handler ```ts const permix = createPermix({ onForbidden: ({ reply }) => { reply.status(403).send({ error: 'Custom forbidden message', }) } }) ``` ### Dynamic Error Handler You can also provide a handler that returns different responses based on the entity and actions: ```ts const permix = createPermix({ onForbidden: ({ reply, entity, actions }) => { if (entity === 'post' && actions.includes('create')) { reply.status(403).send({ error: `You don't have permission to ${actions.join('/')} a ${entity}`, }) return } reply.status(403).send({ error: 'You do not have permission to perform this action', }) } }) ``` The `onForbidden` handler receives: * `request`: Fastify Request object * `reply`: Fastify Reply object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts fastify.register(permix.plugin(async ({ request }) => { // Fetch user permissions from database const userPermissions = await getUserPermissions(request.user.id) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts } } })) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts fastify.put('/posts/:id', { preHandler: async (request, reply) => { const post = await getPostById(request.params.id) const { check } = permix.get(request, reply) // Check if user can update this specific post if (check('post', 'update', post)) { return } else { reply.status(403).send({ error: 'You cannot update this post' }) } } }) ``` # Hono URL: https://permix.letstri.dev/docs/integrations/hono Learn how to use Permix with Hono *** title: Hono description: Learn how to use Permix with Hono ---------------------------------------------- ## Overview Permix provides middleware for Hono that allows you to easily check permissions in your routes. The middleware can be created using the `createPermix` function. Before getting started with Hono integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with Hono: ```ts twoslash import { Hono } from 'hono' import { createPermix } from 'permix/hono' interface Post { id: string authorId: string title: string content: string } // Initialize Hono const app = new Hono() // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' | 'delete' } }>() // Set up the middleware with your permission rules app.use(permix.setupMiddleware(({ c }) => { // You can access c.get('user') or other properties to determine permissions const user = c.get('user') const isAdmin = user?.role === 'admin' return { post: { create: true, read: true, update: isAdmin, delete: isAdmin } } })) ``` The middleware preserves full type safety from your Permix definition, ensuring your permission checks are type-safe. ## Checking Permissions Use the `checkMiddleware` function in your Hono routes to check permissions: ```ts app.post('/posts', permix.checkMiddleware('post', 'create'), (c) => { // Create post logic here return c.json({ success: true }) }) // Check multiple actions app.put('/posts/:id', permix.checkMiddleware('post', ['read', 'update']), (c) => { // Update post logic here return c.json({ success: true }) }) // Check all actions app.delete('/posts/:id', permix.checkMiddleware('post', 'all'), (c) => { // Delete post logic here return c.json({ success: true }) }) ``` ## Accessing Permix Directly You can access the Permix instance directly in your route handlers using the `get` function: ```ts app.get('/posts', (c) => { const permix = permix.get(c) // Check permissions manually if (permix.check('post', 'read')) { // User has permission to read posts return c.json({ posts: getAllPosts() }) } else { return c.json({ error: 'You do not have permission to read posts' }, 403) } }) ``` The `get` function returns the Permix instance with available methods. ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true } }) // Create a template for regular user permissions const userTemplate = permix.template({ post: { create: true, read: true, update: false, delete: false } }) // Use templates in your middleware app.use(permix.setupMiddleware(({ c }) => { const user = c.get('user') if (user?.role === 'admin') { return adminTemplate } return userTemplate })) ``` ## Custom Error Handling By default, the middleware returns a 403 Forbidden response. You can customize this behavior by providing an `onForbidden` handler: ### Basic Error Handler ```ts const permix = createPermix({ onForbidden: ({ c }) => { return c.json({ error: 'Custom forbidden message' }, 403) } }) ``` ### Dynamic Error Handler You can also provide a handler that returns different responses based on the entity and actions: ```ts const permix = createPermix({ onForbidden: ({ c, entity, actions }) => { if (entity === 'post' && actions.includes('create')) { return c.json({ error: `You don't have permission to ${actions.join('/')} a ${entity}` }, 403) } return c.json({ error: 'You do not have permission to perform this action' }, 403) } }) ``` The `onForbidden` handler receives: * `c`: Hono Context object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts app.use(permix.setupMiddleware(async ({ c }) => { // Fetch user permissions from database const user = c.get('user') const userPermissions = await getUserPermissions(user.id) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } } })) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts app.put('/posts/:id', async (c) => { const postId = c.req.param('id') const post = await getPostById(postId) const permix = permix.get(c) // Check if user can update this specific post if (permix.check('post', 'update', post)) { // Update post logic return c.json({ success: true }) } else { return c.json({ error: 'You cannot update this post' }, 403) } }) ``` # Node.js URL: https://permix.letstri.dev/docs/integrations/node Learn how to use Permix with Node.js HTTP servers *** title: Node.js description: Learn how to use Permix with Node.js HTTP servers -------------------------------------------------------------- ## Overview Permix provides middleware for Node.js HTTP servers that allows you to easily check permissions in your request handlers. The middleware can be created using the `createPermix` function. Before getting started with Node.js integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with a Node.js HTTP server: ```ts twoslash import http from 'node:http' import { createPermix } from 'permix/node' interface Post { id: string authorId: string title: string content: string } // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' | 'delete' } }>() // Create an HTTP server const server = http.createServer(async (req, res) => { // Parse the URL const url = new URL(req.url || '/', `http://${req.headers.host}`) const path = url.pathname const method = req.method || 'GET' const context = { req, res } // Setup Permix with permission rules await permix.setupMiddleware(({ req }) => { // Determine user permissions (e.g., from headers, auth token, etc.) const isAdmin = req.headers['x-user-role'] === 'admin' return { post: { create: true, read: true, update: isAdmin, delete: isAdmin } } })(context) // Route handling if (path === '/posts' && method === 'POST') { await permix.checkMiddleware('post', 'create')(context) } else if (path.startsWith('/posts/') && method === 'PUT') { await permix.checkMiddleware('post', ['read', 'update'])(context) } else if (path.startsWith('/posts/') && method === 'DELETE') { await permix.checkMiddleware('post', 'delete')(context) } else { res.statusCode = 404 res.end(JSON.stringify({ error: 'Not found' })) } }) server.listen(3000, () => { console.log('Server running at http://localhost:3000/') }) ``` The middleware preserves full type safety from your Permix definition, ensuring your permission checks are type-safe. ## Checking Permissions Use the `checkMiddleware` function to check permissions for specific routes: ```ts // Check a single action await permix.checkMiddleware('post', 'create')(context) // Check multiple actions await permix.checkMiddleware('post', ['read', 'update'])(context) // Check all actions await permix.checkMiddleware('post', 'all')(context) ``` ## Accessing Permix Directly You can access the Permix instance directly in your request handlers using the `get` function: ```ts http.createServer((req, res) => { const { check } = permix.get(req, res) // User has permission to read posts if (check('post', 'read')) { res.statusCode = 200 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ posts: getAllPosts() })) } }) ``` The `get` function returns the Permix instance with available methods. ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true } }) // Create a template for regular user permissions const userTemplate = permix.template({ post: { create: true, read: true, update: false, delete: false } }) // Use templates in your middleware permix.setupMiddleware(({ req }) => { const isAdmin = req.headers['x-user-role'] === 'admin' if (isAdmin) { return adminTemplate } return userTemplate })(context) ``` ## Custom Error Handling By default, the middleware returns a 403 Forbidden response if the user doesn't have permission. You can customize this behavior by providing an `onForbidden` handler: ### Basic Error Handler ```ts const permix = createPermix({ onForbidden: ({ res }) => { res.statusCode = 403 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ error: 'Custom forbidden message' })) } }) ``` ### Dynamic Error Handler You can also provide a handler that returns different responses based on the entity and actions: ```ts const permix = createPermix({ onForbidden: ({ res, entity, actions }) => { res.statusCode = 403 res.setHeader('Content-Type', 'application/json') if (entity === 'post' && actions.includes('create')) { res.end(JSON.stringify({ error: `You don't have permission to ${actions.join('/')} a ${entity}` })) return } res.end(JSON.stringify({ error: 'You do not have permission to perform this action' })) } }) ``` The `onForbidden` handler receives: * `req`: Node.js IncomingMessage object * `res`: Node.js ServerResponse object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts permix.setupMiddleware(async ({ req }) => { // Extract user ID from request const userId = req.headers['x-user-id'] // Fetch user permissions from database const userPermissions = await getUserPermissions(userId) return { post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } } })(context) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts http.createServer(async (req, res) => { // Setup Permix middleware first... // Extract post ID from URL const url = new URL(req.url || '/', `http://${req.headers.host}`) const pathParts = url.pathname.split('/') const postId = pathParts[2] // e.g., /posts/123 if (req.method === 'PUT' && pathParts[1] === 'posts' && postId) { // Fetch the post data const post = await getPostById(postId) // Get Permix instance const { check } = permix.get(req, res) // Check if user can update this specific post if (check('post', 'update', post)) { // Process update... res.statusCode = 200 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ success: true })) } else { res.statusCode = 403 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ error: 'You cannot update this post' })) } } }) ``` ## Integration with Web Frameworks This integration is designed for raw Node.js HTTP servers. If you're using a web framework: * For Express, use [Permix Express integration](/docs/integrations/express) * For Hono, use [Permix Hono integration](/docs/integrations/hono) # oRPC URL: https://permix.letstri.dev/docs/integrations/orpc Learn how to use Permix with oRPC *** title: oRPC description: Learn how to use Permix with oRPC ---------------------------------------------- ## Overview Permix provides a middleware for oRPC that allows you to easily check permissions in your middlewares. The middleware can be created using the `createPermix` function. Before getting started with oRPC integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with oRPC: ```ts twoslash import { os } from '@orpc/server' import { createPermix } from 'permix/orpc' interface Post { id: string title: string } // Initialize oRPC with context interface Context { user: { id: string role: string } } const orpcPermix = os.$context() // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' } user: { action: 'delete' } }>() // Create a protected middleware with Permix const protectedMiddleware = orpcPermix.use(({ context, next }) => { // You can access context.user or other properties to determine permissions const isAdmin = context.user.role === 'admin' const p = permix.setup({ post: { create: true, read: true, update: isAdmin }, user: { delete: isAdmin } }) return next({ context: { permix: p } }) }) ``` The middleware preserves the context and input types from your oRPC middlewares, ensuring end-to-end type safety in your API. ## Checking Permissions Use the `checkMiddleware` function in your oRPC middlewares to check permissions: ```ts const router = orpcPermix.router({ createPost: protectedMiddleware .use(permix.checkMiddleware('post', 'create')) .handler(({ context }) => { // Create post logic here return { success: true } }), updatePost: protectedMiddleware .use(permix.checkMiddleware('post', ['read', 'update'])) .handler(({ context }) => { // Update post logic here return { success: true } }), deleteUser: protectedMiddleware .use(permix.checkMiddleware('user', 'delete')) .handler(({ context }) => { // Delete user logic here return { success: true } }) }) ``` ## Accessing Permix in Middlewares Permix is automatically added to your oRPC context, so you can access it directly: ```ts const router = orpcPermix.router({ getPosts: protectedMiddleware .handler(({ context }) => { // Check permissions manually if (context.permix.check('post', 'read')) { // User has permission to read posts return getAllPosts() } // If not explicitly blocked by middleware, you can handle permission failures here throw new ORPCError('FORBIDDEN', { message: 'You do not have permission to read posts' }) }) }) ``` The `context.permix` object contains one method: * `check`: Synchronously check a permission ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true }, user: { delete: true } }) // Create a template for regular user permissions const userTemplate = permix.template({ post: { create: true, read: true, update: false }, user: { delete: false } }) // Use templates in your middleware const protectedMiddleware = orpcPermix.use(({ context, next }) => { const p = permix.setup( context.user.role === 'admin' ? adminTemplate : userTemplate ) return next({ context: { permix: p } }) }) ``` ## Custom Error Handling By default, the middleware throws an `ORPCError` with code `FORBIDDEN`. You can customize this behavior by providing a `forbiddenError` option: ### Static Error ```ts const permix = createPermix({ forbiddenError: new ORPCError('FORBIDDEN', { message: 'Custom forbidden message' }) }) ``` ### Dynamic Error You can also provide a function that returns different errors based on the entity and actions: ```ts const permix = createPermix({ forbiddenError: ({ entity, actions, context }) => { if (entity === 'post' && actions.includes('create')) { return new ORPCError('FORBIDDEN', { message: `User ${context.user.id} doesn't have permission to ${actions.join('/')} a ${entity}` }) } return new ORPCError('FORBIDDEN', { message: 'You do not have permission to perform this action' }) } }) ``` The `forbiddenError` handler receives: * `context`: Your oRPC context object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts const protectedMiddleware = orpcPermix.use(async ({ context, next }) => { // Fetch user permissions from database const userPermissions = await getUserPermissions(context.user.id) const p = permix.setup({ post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts }, user: { delete: userPermissions.canDeleteUsers } }) return next({ context: { permix: p } }) }) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts const router = orpcPermix.router({ updatePost: protectedMiddleware .handler(async ({ input, context }) => { const post = await getPostById(input.postId) // Check if user can update this specific post if (context.permix.check('post', 'update', post)) { // Update post logic return { success: true } } throw new ORPCError('FORBIDDEN', { message: 'You cannot update this specific post' }) }) }) ``` # React URL: https://permix.letstri.dev/docs/integrations/react Learn how to use Permix with React applications *** title: React description: Learn how to use Permix with React applications ------------------------------------------------------------ ## Overview Permix provides official React integration through the `PermixProvider` component and `usePermix` hook. This allows you to manage permissions reactively in your React app. Before getting started with React integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup First, wrap your application with the `PermixProvider`: ```tsx title="App.tsx" import { PermixProvider } from 'permix/react' import { permix } from './lib/permix' function App() { return ( ) } ``` Remember to always pass the same Permix instance to both the `PermixProvider` and `usePermix` hook to maintain type safety. ## Hook For checking permissions in your components, you can use the `usePermix` hook. And to avoid importing the hook and Permix instance in every component, you can create a custom hook: ```tsx title="hooks/use-permissions.ts" import { usePermix } from 'permix/react' import { permix } from '../lib/permix' export function usePermissions() { return usePermix(permix) } ``` ## Components If you prefer using components, you can import the `createComponents` function from `permix/react` and create checking components: ```ts title="lib/permix.ts" import { createComponents } from 'permix/react' // ... export const { Check } = createComponents(permix) ``` And then you can use the `Check` component in your components: ```tsx title="page.tsx" export default function Page() { return ( Will show this if a user doesn't have permission

} // Will show this if a user doesn't have permission reverse // Will flip the logic of the permission check > Will show this if a user has permission
) } ```
## Usage Use the `usePermix` hook and checking components in your components: ```tsx title="page.tsx" import { usePermix } from 'permix/react' import { permix } from './lib/permix' import { Check } from './lib/permix-components' export default function Page() { const post = usePost() const { check, isReady } = usePermix(permix) if (!isReady) { return
Loading permissions...
} const canEdit = check('post', 'edit', post) return (
{canEdit ? ( ) : (

You don't have permission to edit this post

)} Can I create a post inside the Check component?
) } ```
## Example You can find the example of the React integration [here](https://github.com/letstri/permix/tree/main/examples/react).
# Solid URL: https://permix.letstri.dev/docs/integrations/solid Learn how to use Permix with Solid applications *** title: Solid description: Learn how to use Permix with Solid applications ------------------------------------------------------------ ## Overview Permix provides official Solid integration through the `PermixProvider` component and `usePermix` hook. This allows you to manage permissions reactively in your Solid app. Before getting started with Solid integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup First, wrap your application with the `PermixProvider`: ```tsx title="App.tsx" import { PermixProvider } from 'permix/solid' import { permix } from './lib/permix' function App() { return ( ) } ``` Remember to always pass the same Permix instance to both the `PermixProvider` and `usePermix` hook to maintain type safety. ## Hook For checking permissions in your components, you can use the `usePermix` hook. And to avoid importing the hook and Permix instance in every component, you can create a custom utility: ```tsx title="hooks/use-permissions.ts" import { usePermix } from 'permix/solid' import { permix } from '../lib/permix' export function usePermissions() { return usePermix(permix) } ``` ## Components If you prefer using components, you can import the `createComponents` function from `permix/solid` and create checking components: ```ts title="lib/permix.ts" import { createComponents } from 'permix/solid' // ... export const { Check } = createComponents(permix) ``` And then you can use the `Check` component in your components: ```tsx title="page.tsx" export default function Page() { return ( Will show this if a user doesn't have permission

} // Will show this if a user doesn't have permission reverse // Will flip the logic of the permission check > Will show this if a user has permission
) } ```
## Usage Use the `usePermix` hook and checking components in your components: ```tsx title="page.tsx" import { usePermix } from 'permix/solid' import { permix } from './lib/permix' import { Check } from './lib/permix-components' export default function Page() { const post = usePost() const { check, isReady } = usePermix(permix) const canEdit = () => check('post', 'edit', post) return ( <> {!isReady() ?
Loading permissions...
: (
{canEdit() ? ( ) : (

You don't have permission to edit this post

)} Can I create a post inside the Check component?
) } ) } ```
## Example You can find the example of the Solid integration [here](https://github.com/letstri/permix/tree/main/examples/solid).
# tRPC URL: https://permix.letstri.dev/docs/integrations/trpc Learn how to use Permix with tRPC *** title: tRPC description: Learn how to use Permix with tRPC ---------------------------------------------- ## Overview Permix provides a middleware for tRPC that allows you to easily check permissions in your procedures. The middleware can be created using the `createPermix` function. Before getting started with tRPC integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup Here's a basic example of how to use the Permix middleware with tRPC: ```ts twoslash import { initTRPC } from '@trpc/server' import { createPermix } from 'permix/trpc' interface Post { id: string authorId: string title: string content: string } interface Context { user: { id: string role: string } } // Initialize tRPC const t = initTRPC.context().create() // Create your Permix instance const permix = createPermix<{ post: { dataType: Post action: 'create' | 'read' | 'update' | 'delete' } }>() // Create a protected procedure with Permix const protectedProcedure = t.procedure.use(({ ctx, next }) => { // You can access ctx.user or other properties to determine permissions const isAdmin = ctx.user.role === 'admin' const p = permix.setup({ post: { create: true, read: true, update: isAdmin, delete: isAdmin } }) return next({ ctx: { permix: p, }, }) }) ``` The middleware preserves the context and input types from your tRPC procedures, ensuring end-to-end type safety in your API. ## Checking Permissions Use the `checkMiddleware` function in your tRPC procedures to check permissions: ```ts const router = t.router({ createPost: protectedProcedure .use(permix.checkMiddleware('post', 'create')) .mutation(({ input }) => { // Create post logic here return { success: true } }), updatePost: protectedProcedure .use(permix.checkMiddleware('post', ['read', 'update'])) .mutation(({ input }) => { // Update post logic here return { success: true } }), deletePost: protectedProcedure .use(permix.checkMiddleware('post', 'delete')) .mutation(({ input }) => { // Delete post logic here return { success: true } }), adminAction: protectedProcedure .use(permix.checkMiddleware('post', 'all')) .query(() => { // Admin-only action return { success: true } }) }) ``` ## Accessing Permix in Procedures Permix is automatically added to your tRPC context, so you can access it directly: ```ts const router = t.router({ getPosts: protectedProcedure .query(({ ctx }) => { // Check permissions manually if (ctx.permix.check('post', 'read')) { // User has permission to read posts return getAllPosts() } // If not explicitly blocked by middleware, you can handle permission failures here throw new TRPCError({ code: 'FORBIDDEN', message: 'You do not have permission to read posts' }) }) }) ``` The `ctx.permix` object contains one method: * `check`: Synchronously check a permission ## Using Templates Permix provides a template helper to create reusable permission rule sets: ```ts // Create a template for admin permissions const adminTemplate = permix.template({ post: { create: true, read: true, update: true, delete: true } }) // Create a template for regular user permissions const userTemplate = permix.template({ post: { create: true, read: true, update: false, delete: false } }) // Use templates in your middleware const protectedProcedure = t.procedure.use(({ ctx, next }) => { const p = permix.setup( ctx.user.role === 'admin' ? adminTemplate : userTemplate ) return next({ ctx: { permix: p, }, }) }) ``` ## Custom Error Handling By default, the middleware throws a `TRPCError` with code `FORBIDDEN`. You can customize this behavior by providing a `forbiddenError` option: ### Static Error ```ts const permix = createPermix({ forbiddenError: new TRPCError({ code: 'FORBIDDEN', message: 'Custom forbidden message', }) }) ``` ### Dynamic Error You can also provide a function that returns different errors based on the entity and actions: ```ts const permix = createPermix({ forbiddenError: ({ entity, actions, ctx }) => { if (entity === 'post' && actions.includes('create')) { return new TRPCError({ code: 'FORBIDDEN', message: `User ${ctx.user.id} doesn't have permission to ${actions.join('/')} a ${entity}`, }) } return new TRPCError({ code: 'FORBIDDEN', message: 'You do not have permission to perform this action', }) }, }) ``` The `forbiddenError` handler receives: * `ctx`: Your tRPC context object * `entity`: The entity that was checked * `actions`: Array of actions that were checked ## Advanced Usage ### Async Permission Rules You can use async functions in your permission setup: ```ts const protectedProcedure = t.procedure.use(async ({ ctx, next }) => { // Fetch user permissions from database const userPermissions = await getUserPermissions(ctx.user.id) const p = permix.setup({ post: { create: userPermissions.canCreatePosts, read: userPermissions.canReadPosts, update: userPermissions.canUpdatePosts, delete: userPermissions.canDeletePosts } }) return next({ ctx: { permix: p, }, }) }) ``` ### Dynamic Data-Based Permissions You can check permissions based on the specific data being accessed: ```ts const router = t.router({ updatePost: protectedProcedure .input(z.object({ postId: z.string() })) .mutation(async ({ input, ctx }) => { const post = await getPostById(input.postId) // Check if user can update this specific post if (ctx.permix.check('post', 'update', post)) { // Update post logic return { success: true } } throw new TRPCError({ code: 'FORBIDDEN', message: 'You cannot update this specific post' }) }) }) ``` # Vue URL: https://permix.letstri.dev/docs/integrations/vue Learn how to use Permix with Vue applications *** title: Vue description: Learn how to use Permix with Vue applications ---------------------------------------------------------- ## Overview Permix provides official Vue integration through the `permixPlugin` and `usePermix` composable. This allows you to manage permissions reactively in your Vue app. Before getting started with Vue integration, make sure you've completed the initial setup steps in the [Quick Start](/docs/quick-start) guide. ## Setup First, install the Vue plugin in your application: ```ts title="main.ts" import { createApp } from 'vue' import { permixPlugin } from 'permix/vue' import { permix } from './lib/permix' import App from './App.vue' const app = createApp(App) app.use(permixPlugin, { permix }) app.mount('#app') ``` Remember to always use the same Permix instance here and in the `usePermix` composable to maintain type safety. ## Composable For checking permissions in your components, you can use the `usePermix` composable. And to avoid importing the composable and Permix instance in every component, you can create a custom composable: ```ts title="composables/use-permissions.ts" import { usePermix } from 'permix/vue' import { permix } from './lib/permix' export function usePermissions() { return usePermix(permix) } ``` ## Components If you prefer using components, you can import the `createComponents` function from `permix/vue` and create checking components: ```ts title="lib/permix.ts" import { createComponents } from 'permix/vue' // ... export const { Check } = createComponents(permix) ``` And then you can use the `Check` component in your templates: ```vue title="page.vue" ``` ## Usage Use the `usePermix` composable in your components to check permissions: ```vue title="page.vue" ``` ## Example You can find the example of the Vue integration [here](https://github.com/letstri/permix/tree/main/examples/vue).