# 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
)}
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"
Will show this if a user have permission
Will show this if a user doesn't have permission
```
## Usage
Use the `usePermix` composable in your components to check permissions:
```vue title="page.vue"
Loading permissions...
You don't have permission to edit this post
Can I edit this post?
You don't have permission to edit this post
```
## Example
You can find the example of the Vue integration [here](https://github.com/letstri/permix/tree/main/examples/vue).