Roles
A role is a named label — admin, editor, moderator — that you attach to users. A role can carry any number of permissions. When you check permission.can('edit-posts'), the package automatically checks all permissions assigned to every role the user holds.
Creating roles
Roles are created automatically the first time you assign them. There is no need to pre-register them.
// This creates the 'admin' role if it doesn't exist, then assigns it to the user
await permission.assign.role(userId, 'admin')Assigning roles to users
await permission.assign.role(userId, 'admin')
await permission.assign.role(userId, 'editor')
await permission.assign.role(userId, 'moderator')Assigning a role the user already has is safe — it is a no-op.
Revoking roles
await permission.revoke.role(userId, 'editor')WARNING
Revoking a role that was never assigned (or does not exist) throws a PermissionError with code NOT_FOUND. Wrap in try/catch if unsure.
import { PermissionError } from '@actinode/express-permission'
try {
await permission.revoke.role(userId, 'ghost-role')
} catch (err) {
if (err instanceof PermissionError) {
console.log(err.code) // 'NOT_FOUND'
console.log(err.message) // 'Role "ghost-role" not found'
}
}Getting all roles for a user
const roles = await permission.get.roles(userId)
// ['admin', 'editor']Checking roles in middleware
Use permission.hasRole() to protect a route so that only users with that role can access it.
import { permission } from './permission.js'
// Only admins
app.delete('/users/:id',
permission.hasRole('admin'),
async (req, res) => {
await deleteUser(req.params.id)
res.json({ deleted: true })
},
)
// Only moderators
app.post('/posts/:id/hide',
permission.hasRole('moderator'),
async (req, res) => {
await hidePost(req.params.id)
res.json({ hidden: true })
},
)Unauthorized (no userId in request):
{ "error": "Unauthorized", "message": "User not authenticated" }HTTP status: 401
Forbidden (user lacks the role):
{ "error": "Forbidden", "message": "Missing role: admin" }HTTP status: 403
Checking roles via helpers
Use permission.check.hasRole() outside of middleware — for example, in business logic or service functions.
const isAdmin = await permission.check.hasRole(userId, 'admin')
if (isAdmin) {
// perform privileged action
}Assigning permissions to a role
Permissions on roles are managed at the database/adapter level. With the Prisma adapter you can link a permission to a role directly in your seed script or migration:
// Seed: ensure the role + permission records exist, then link them
const adminRole = await prisma.role.upsert({
where: { name: 'admin' },
create: { name: 'admin' },
update: {},
})
const perm = await prisma.permission.upsert({
where: { name: 'delete-posts' },
create: { name: 'delete-posts' },
update: {},
})
await prisma.role.update({
where: { id: adminRole.id },
data: { permissions: { connect: { id: perm.id } } },
})With the Mongoose adapter, the Role document stores permissions as a String[]:
await Role.findOneAndUpdate(
{ name: 'admin' },
{ $addToSet: { permissions: 'delete-posts' } },
{ upsert: true },
)Once a permission is attached to the role, any user with that role will pass permission.can('delete-posts') and permission.check.can(userId, 'delete-posts') automatically.
Real-world example
// Seed roles with permissions
const roles = {
admin: ['view-posts', 'edit-posts', 'delete-posts', 'manage-users'],
editor: ['view-posts', 'edit-posts', 'publish-posts'],
viewer: ['view-posts'],
}
for (const [roleName, perms] of Object.entries(roles)) {
// ... upsert role and link permissions
}
// Assign roles to users at registration or promotion
await permission.assign.role(newUserId, 'viewer')
await permission.assign.role(promotedUserId, 'editor')