Skip to content

Hono

@vperms/hono is the Hono counterpart of the Express integration. It sets the ability on the Hono context instead of the request object.

import { Hono } from "hono";
import {
vpermsMiddleware,
hasPermission,
type VPermsEnv,
} from "@vperms/hono";
import { VeguiPermsMemoryAdapter } from "vperms";
const app = new Hono<VPermsEnv>();
app.use(
vpermsMiddleware({
adapter: new VeguiPermsMemoryAdapter(),
workspace: "workspace",
resolver: (c) => c.get("session")?.userId ?? null,
}),
);
app.get("/posts/:id", hasPermission("posts.read"), (c) =>
c.json({ allowed: true }),
);
interface VpermsMiddlewareOptions {
adapter: VeguiPermsAdapter;
resolver: SubjectResolver;
workspace: string | WorkspaceResolver;
defaultParents?: DefaultParents;
permissionsExport?: { path: string };
}
type VPermsContext = Context<VPermsEnv>;
type SubjectResolver =
(c: VPermsContext) => SubjectId | Principal | null | Promise<...>;
type WorkspaceResolver = (c: VPermsContext) => string | Promise<string>;

The middleware stores three context variables:

interface VPermsVariables {
ability: RequestAbility;
subject: Subject | undefined;
kind: SubjectType | undefined;
}

Use VPermsEnv as the app’s generic so c.get("ability") is typed.

import { hasPermission, hasAnyPermission } from "@vperms/hono";
app.get("/posts/:id", hasPermission("posts.read"), handler);
app.delete("/posts/:id", hasAnyPermission("admin", "posts.delete"), handler);

hasPermission requires all inputs, hasAnyPermission at least one. Both return a bare 403 response (c.body(null, 403)) when denied. Permissions can be functions:

import type { PermissionBuilder } from "@vperms/hono";
const ownsPost: PermissionBuilder = (c) => `posts.${c.req.param("id")}.write`;
app.put("/posts/:id", hasPermission(ownsPost), handler);

With permissionsExport.path set, a matching GET is answered as JSON:

Status Body Meaning
200 ResolvedSubject Success
403 empty Caller cannot read the target
404 empty Subject does not exist
400 { "error": string } Invalid subject id

Other requests continue through await next().

RequestAbility, VPermsEnv, VPermsVariables, VPermsContext, vpermsMiddleware, hasPermission, hasAnyPermission, PermissionBuilder, PermissionInput, SubjectResolver, SubjectResolverResult, WorkspaceResolver, PermissionsExportOptions, VpermsMiddlewareOptions, ANONYMOUS_SUBJECT_ID.