franshiro-custom-admin
v0.1.0
Published
Dynamic admin panel for Vue 3 + Express + Drizzle ORM. Auto-generates CRUD from your Drizzle schema; drop in custom Vue components with one line.
Maintainers
Readme
franshiro-custom-admin
A dynamic admin panel for Vue 3 + Express + Drizzle ORM. Point it at your Drizzle schema and get list / create / edit / detail / delete screens automatically — then override any field with a custom .vue component in one line.
- Schema-driven — the server introspects your Drizzle tables (Postgres / MySQL / SQLite) and generates a CRUD API.
- Isomorphic config — one
defineResource()config, shared by server and client. - Custom components — any
.vuefollowingFieldWidgetPropsdrops in viacomponent: MyWidget. - RBAC + validation — per-resource access rules and Zod validation enforced server-side.
- One install, three entry points —
franshiro-custom-admin/core,/server,/vue. Backend code never bundles Vue; frontend never bundles Express/Drizzle.
Install
npm install franshiro-custom-adminPeer dependencies (install the ones you use): drizzle-orm + express (backend), vue (frontend).
Quick start
1. Shared config (resources.ts) — imported by both server and client:
import { defineResource } from 'franshiro-custom-admin/core'
import { posts, users } from './schema' // your Drizzle tables
export const resources = [
defineResource({
table: posts,
fields: {
status: { type: 'enum', options: ['draft', 'published'] },
authorId: { type: 'relation', relation: { table: users, labelColumn: 'name' } },
},
list: { columns: ['id', 'title', 'status'], searchable: ['title'], filterable: ['status'] },
access: { view: (ctx) => ctx.can('post:read'), create: (ctx) => ctx.role === 'admin' },
}),
]2. Backend (Express):
import { createAdminRouter } from 'franshiro-custom-admin/server'
import { resources } from './resources'
import { db } from './db'
app.use('/admin/api', yourAuthMiddleware, createAdminRouter({
db,
resources,
getContext: (req) => ({ userId: req.user.id, role: req.user.role, can: (p) => req.user.can(p) }),
}))3. Frontend (Vue):
import { createAdmin } from 'franshiro-custom-admin/vue'
import { resources } from './resources'
app.use(createAdmin({ resources, apiBaseUrl: '/admin/api' }))
// then place <AdminPanel /> in a templateTailwind: the widgets use utility classes but ship no CSS — add the package to your Tailwind content:
'./node_modules/franshiro-custom-admin/dist/vue.js'.
Custom field components
Any .vue satisfying the widget contract drops in:
<script setup lang="ts">
import type { FieldWidgetProps } from 'franshiro-custom-admin/vue'
defineProps<FieldWidgetProps<string>>()
const emit = defineEmits<{ 'update:modelValue': [string] }>()
</script>fields: { body: { component: MarkdownEditor } }Because the shared config is also imported by the Node server, use a small .vue stub loader server-side (or a client-only component layer). See the examples/basic README for a working setup.
Status
MVP (v0.1). Deferred: many-to-many relations, built-in dashboard/login, URL-synced routing, dialects beyond PG/MySQL/SQLite. See the repo roadmap.
License
MIT © franshiro
