nuxt-supabase-backoffice
v0.1.3
Published
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'].
Downloads
548
Readme
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-backofficeThen 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}/newand${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).
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>. BoConfigplugin 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.
