Better Auth
Learn how to use Permix with Better Auth
Overview
Permix provides a Better Auth plugin that exposes a GET /permix/get-permissions endpoint, returning the current user's dehydrated permissions for easy server-client sync.
Make sure you've completed the Quick Start guide first.
Server Setup
Add permixPlugin<Definition>() to your auth config and call createPermix() to define your rules.
import { } from 'better-auth'
import { , } from 'permix/better-auth'
import type { } from 'permix'
interface Post {
: string
: string
: string
: string
}
export type = <{
: {
: Post
: 'create' | 'read' | 'update' | 'delete'
}
}>
export const = ({
// ...your config
: {
: {
: { : 'string', : 'user' },
},
},
: [<>()],
})
// Optional but recommended: pass session type for typed fields like `role`
type = typeof ..
export const = <, >({
: ({ }) => ({
: {
: . === 'admin',
: true,
: . === 'admin',
: . === 'admin',
},
}),
})The second generic Session defaults to Better Auth's base session type. Passing typeof auth.$Infer.Session is optional but recommended when using additionalFields or plugins that extend the session (e.g. admin, organization).
Passing Definition to permixPlugin() and permixClient() ensures auth.api.getPermissions() and authClient.permix.getPermissions() return typed PermixStateJSON<Definition>.
Client Setup
Add permixClient() to your auth client for typed access to the permissions endpoint:
import { createAuthClient } from 'better-auth/client'
import { permixClient } from 'permix/better-auth'
const authClient = createAuthClient({
plugins: [permixClient<Definition>()],
})
const { data: permissions, error } = await authClient.permix.getPermissions()Hydrating on the Client
Hydrate the dehydrated permissions into a client-side Permix instance. Works with React, Vue, and Solid:
import { createPermix } from 'permix'
import type { Definition } from './permix'
const permix = createPermix<Definition>()
const { data } = await authClient.permix.getPermissions()
if (data) {
permix.hydrate(data)
}See the Hydration guide for SSR details.
Using Templates
Use standalone createTemplate() for reusable rule sets. Unlike permix.template(), it avoids circular reference issues with createPermix():
import { createTemplate } from 'permix'
import { createPermix } from 'permix/better-auth'
const adminTemplate = createTemplate<Definition>({
post: {
create: true,
read: true,
update: true,
delete: true,
},
})
const userTemplate = createTemplate<Definition>({
post: {
create: false,
read: true,
update: false,
delete: false,
},
})
type Session = typeof auth.$Infer.Session
const permix = createPermix<Definition, Session>({
rules: ({ user }) => {
if (user.role === 'admin') {
return adminTemplate()
}
return userTemplate()
},
})Reusing Rules with Framework Middleware
The rules function from createPermix() can be reused in framework middleware to protect routes beyond Better Auth endpoints:
import { Hono } from 'hono'
import { createPermix as createHonoPermix } from 'permix/hono'
import { auth, permix } from './auth'
const honoPermix = createHonoPermix<Definition>()
const app = new Hono()
app.use(honoPermix.setupMiddleware(async ({ c }) => {
const session = await auth.api.getSession({
headers: c.req.raw.headers,
})
if (!session) {
return {
post: { create: false, read: false, update: false, delete: false },
}
}
return permix.rules(session)
}))
app.post('/posts', honoPermix.checkMiddleware('post', 'create'), (c) => {
return c.json({ success: true })
})Advanced Usage
Async Rules
The rules callback supports async functions for permissions that depend on external data:
const permix = createPermix<Definition, Session>({
rules: async ({ user }) => {
const teamPermissions = await getTeamPermissions(user.id)
return {
post: {
create: teamPermissions.canCreatePosts,
read: true,
update: teamPermissions.canUpdatePosts,
delete: teamPermissions.canDeletePosts,
},
}
},
})Admin Plugin
With Better Auth's admin plugin, use typeof auth.$Infer.Session to get the typed role field:
import { betterAuth } from 'better-auth'
import { admin } from 'better-auth/plugins'
import { permixPlugin, createPermix } from 'permix/better-auth'
export const auth = betterAuth({
plugins: [admin(), permixPlugin<Definition>()],
})
type Session = typeof auth.$Infer.Session
const permix = createPermix<Definition, Session>({
rules: ({ user }) => ({
post: {
create: user.role === 'admin' || user.role === 'editor',
read: true,
update: user.role === 'admin' || user.role === 'editor',
delete: user.role === 'admin',
},
}),
})Example
For a full working example, see the Next.js + Better Auth example.