Roles
Helper functions and components to work with roles.
Roles in VitNode are stored in the core_roles table. Every user belongs to exactly one role through their roleId, and a role carries the flags and color used for permissions and display.
A role exposes the following flags:
| Field | Description |
|---|---|
protected | Built-in role that cannot be deleted. |
default | Role assigned to newly registered users. |
root | Super-admin role with full access to everything. |
guest | Role representing non-authenticated visitors. |
color | Color used to style the members of the role (nullable). |
Role names are translatable
A role has no name column. Its display name lives in the core_languages_words
i18n table (seeded per language during database setup), so it can be translated
per locale. Resolve the name for the active language and pass it to the
components below.
Backend
Reading a user's role
Every user references a role through roleId. Load the role by querying core_roles with that id:
import { eq } from "drizzle-orm";
import { buildRoute } from "@vitnode/core/api/lib/route";
import { core_roles } from "@vitnode/core/database/roles";
export const roleRoute = buildRoute({
handler: async c => {
const user = c.get("user");
if (!user) return c.json({ message: "Not signed in" }, 401);
const [role] = await c
.get("db")
.select()
.from(core_roles)
.where(eq(core_roles.id, user.roleId))
.limit(1);
return c.json({ role });
},
});Checking admin access
Membership in an admin role is resolved with checkIfUserIsAdmin from the SessionAdminModel. It returns true when the user — by their own id or their roleId — has admin permission.
import { buildRoute } from "@vitnode/core/api/lib/route";
import { SessionAdminModel } from "@vitnode/core/api/models/session-admin";
export const adminOnlyRoute = buildRoute({
handler: async c => {
const user = c.get("user");
const isAdmin = user
? await new SessionAdminModel(c).checkIfUserIsAdmin(user.id)
: false;
if (!isAdmin) return c.json({ message: "Forbidden" }, 403);
return c.json({ ok: true });
},
});Frontend
On the frontend a user's role is identified by the roleId carried on the session user, available through getSessionApi().
Formatting a role
Use the RoleFormat component to render a role's name styled with its color.
Because role names are translatable, the name is the full list of translations;
RoleFormat resolves it to the active locale with getLocale(), so it is an
async Server Component.
import { RoleFormat } from "@vitnode/core/components/role-format";
<RoleFormat
role={{
id: 2,
name: [
{ languageCode: "en", name: "Administrator" },
{ languageCode: "pl", name: "Administrator" },
],
color: "#ef4444",
}}
/>;The role prop has the following shape:
{
id: number;
name: {
languageCode: string;
name: string;
}[];
color: string | null;
}Server Component only
RoleFormat calls getLocale() from next-intl/server, so it can only be
rendered on the server. The admin roles list route returns the name array in
exactly this shape.
Exposing role metadata
Role details such as color and its flags live in the core_roles table on
the backend. To display them on the frontend, return the data you need from a
backend route and consume it with the fetcher.