nuxt-supabase-backoffice

Self-contained Nuxt 4 module providing a reusable admin/backoffice toolkit (list/edit/view scaffolds, schema-driven forms, foreign-key combobox, m2m junctions, statistics, role gates, audit log, i18n). Consume via modules:['nuxt-supabase-backoffice'].
2,583
Install
npm install nuxt-supabase-backoffice
Latest Version:0.4.1
Vue:3
License:Proprietary
Last Updated:Sep 29, 2026
Links: npm
Maintainer: exworm

nuxt-supabase-backoffice

Self-contained Nuxt 4 module providing a reusable admin/backoffice toolkit. Built with @nuxt/module-builder and published to npm — it ships as JS in dist/, so it installs and runs from node_modules like any module (no extends, no raw-TS-from-node_modules transpile issues).

License: proprietary, all rights reserved — see LICENSE. Public on npm for the copyright holder's own use only; no reuse is granted.

Install

Public package — no registry config or auth required:

pnpm add nuxt-supabase-backoffice

Then register the module — a single entry. It self-registers its components, bo-admin layout, server routes, i18n locales, and ${base}/** ssr:false route rule from within the module; the virtual #bo-* modules, auto-page routes, and per-resource Nitro handlers are wired the same way. No extends.

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-supabase-backoffice'],

  backoffice: {
    base: '/admin',
    apiBase: '/api/admin',
    roles: ['admin', 'superadmin'],
    authResolverPath: '~/server/utils/bo-resolver',
    csrfVerifyPath: '~/server/utils/bo-csrf',
    auditLogPath: '~/server/utils/bo-audit',
    // Alias path to a TS module exporting `{ resources, moduleOptions }`.
    // Resources keep their Zod schemas + function callbacks because the
    // build re-exports this module by absolute path (no JSON serialization).
    resourcesModule: '~~/server/admin/resources',
  },
})

Peer dependencies (declared in package.json): @nuxt/ui ^4 (behavior primitives only — UModal, USlideover, UTooltip, UDropdownMenu, UPopover, UCalendar, UInputTags, useToast), @nuxtjs/supabase ^2, @nuxtjs/i18n ^10, zod ^4, @vueuse/core ^14, @vueuse/integrations ^14, sortablejs ^1.15, @iconify-json/lucide ^1.2.

Three modes

Mode Where you write Pages Endpoints
A. Declarative server/admin/resources/{name}.ts BoResourceDef Auto-registered list + detail Vue routes — no .vue file Generated by the dispatcher
B. Declarative + override A + a .vue page at app/pages/admin/{path}/index.vue File-based page wins on path collision (Mode B escape hatch) Generated
C. Programmatic Custom page + (optional) custom server route Consumer Consumer / Supabase-direct

Feature coverage

  • List + filter + search + sort + pagination + stats cards.
  • Page-based create + edit at ${path}/new and ${path}/[handle]. Delete (soft + hard) keeps a confirm modal with type-to-confirm and a dependents banner.
  • Drag-reorder with keyboard fallback; positional siblings shift atomically.
  • Detail pages with breadcrumb, child-relation tabs, per-record audit-trail tab, and header actions (headerActions).
  • Selectable rows + bulk row actions (selectable: true + bulkActions: [...]). Built-in ops: soft-delete / restore / delete / update. Bulk route auto-registered at ${apiBase}/${path}/bulk. Returns { ok, failed } for partial-failure surfacing.
  • Declarative custom server endpoints (customEndpoints: { '<key>': { bodySchema, handler, audit } }). Auto-registered at ${apiBase}/${path}/<key> with the same auth + CSRF + Zod + audit pipeline as built-in CRUD. Row, bulk, and detail-page actions target the same endpoint by key.
  • Command-palette search per-resource (search: opt-in) — listbox/option semantics with aria-activedescendant, relevance ranking, recents, #resource scoping and > verb commands.
  • Server-side metrics + charts. stats.metrics declares what a figure MEANS (value / trend / partition / top / funnel / progress); the engine aggregates in Postgres via versioned SECURITY INVOKER functions and renders through @unovis/vue behind BoChart* façades. Served from GET {apiBase}/{path}/_stats, gated by the resource's read roles, and computed over the same predicate as the rows beneath it. Install sql/bo_metrics.sql into your database — see §Database contract.
  • Sidebar nav model. Groups declared centrally in moduleOptions.navGroups (label / icon / order / defaultExpanded / static items), entries honour nav.order, collapsible groups persist per group, an in-sidebar filter, and a focus-trapped mobile drawer below 1024px.
  • Nav badges — nav.badge: { where } resolved through one batched GET {apiBase}/_nav-counts, gated per resource by the same read roles as its list, so a badge never reveals the depth of a queue the viewer cannot open.

Slots

BoListPage exposes twelve slots (header, header-actions, stats, before-table, loading, error, empty, cell-{column}, row-actions, after-table, modals). BoResourceDetailPage exposes header-actions, before-content and after-content.

Mode A pages forward all of them. BoAutoListPage and BoAutoDetailPage pass $slots straight through, so a consumer can wrap the auto page and fill one slot instead of re-implementing the page as a Mode B file. (Before 0.4.0 they forwarded none, which made the documented slot surface unreachable for any declaratively-registered resource.)

Theming

All visuals are CSS variables on :root and .dark. Override per consumer:

/* app/assets/styles/bo-overrides.css */
:root {
  --bo-primary: #4f46e5;
  --bo-radius-md: 8px;
}

See app/assets/styles/bo-tokens.css for the full token surface.

Consumer contract

The layer never imports from project-specific paths. Project-specific decisions flow through:

  • Auth resolver (module.authResolverPath) — server-side (event) => Promise<{ id, roles, extra? } | null>.
  • CSRF verifier (module.csrfVerifyPath, optional) — server-side (event) => Promise<true | string>.
  • Audit log callback (module.auditLogPath, optional) — server-side (entry: BoAuditEntry) => Promise<void>.
  • BoConfig plugin injection — client-side runtime providers (CSRF token fetch, Supabase client, auth roles ref, toast impl, error map override).

See CLAUDE.md for the full architecture, modes, resource-def shape, and extension patterns.

Database contract

Each consumer must provision the audit-log table the layer's default writer targets (bo_audit_log, or your backoffice.audit.tableName). Canonical DDL + RLS (default-deny except service_role) ship in supabase/audit_log.canonical.sql — apply it into your declarative schema or as a migration. Set backoffice.audit.enabled = false to opt out, or auditLogPath to ship audit events to a different sink.

Consumers using stats.metrics must also install sql/bo_metrics.sql — three SECURITY INVOKER, format('%I')-quoted functions with EXECUTE revoked from anon and a statement_timeout. They are version-suffixed (_v1) deliberately: upgrading the module must never silently change SQL semantics under an already-installed database. Ship them the same way you ship any other schema change (declarative file and a mirror migration, if that is your workflow).

Version History

Version Released Vue License
0.4.1 ^3.0 Proprietary
0.4.0 ^3.0 Proprietary
0.2.8 ^3.0 Proprietary
0.2.7 ^3.0 Proprietary
0.2.6 ^3.0 Proprietary