Hono
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 guide.
Setup
Here's a basic example of how to use the Permix middleware with Hono:
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:
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:
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:
// 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
const permix = createPermix<Definition>({
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:
const permix = createPermix<Definition>({
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 objectentity
: The entity that was checkedactions
: Array of actions that were checked
Advanced Usage
Async Permission Rules
You can use async functions in your permission setup:
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:
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)
}
})