PRisma ADmin — Modular admin panel library for PostgreSQL
Zero-config CRUD out of the box. Fully customizable when you need it.
Documentation: English | Русский
PRADA turns any PostgreSQL database with a Prisma schema into a fully functional admin panel. It works as a library — not a framework — so you can drop it into an existing Express app, extend it with custom modules, and override any part of the UI.
Use it as a foundation. The database is already connected, CRUD already works, auth is handled. You focus on building your domain-specific admin logic on top.
npm install @blysspeak/prada @blysspeak/prada-uiimport express from 'express'
import { PrismaClient } from '@prisma/client'
import { createPradaServer } from '@blysspeak/prada'
const app = express()
const prisma = new PrismaClient()
app.use('/admin', await createPradaServer({
prisma,
auth: { login: 'admin', password: 'secret' }
}))
app.listen(3000)
// Admin panel at http://localhost:3000/adminThat's it. Every model in your Prisma schema now has list, create, edit, view, and delete pages.
- Zero config — auto-generates CRUD from Prisma schema
- 3-level API — ready solution, building blocks, or primitives
- Module system — extend backend with custom routes and middleware
- UI customization — replace any field, cell, page, or section via a single config
- Data hooks — React Query hooks for custom pages (
useModelList,useModelCreate, etc.) - Multi-file schema — supports directory of
.prismafiles - JWT auth — HTTP-only cookies, refresh tokens, setup wizard
- i18n — Russian and English out of the box
- Themes — dark and light with animated toggle
- Advanced filters — operator-based filtering (contains, gte, lte, in, etc.)
- Bulk operations — mass delete and update via API
- Stats API — per-model record counts and recent activity
- Audit log — automatic change tracking with diff history
- Keyboard shortcuts — navigate with hotkeys,
?for help - Global search — Ctrl+K command palette across all models
- Inline editing — double-click cells to edit in place
- Relations UI — autocomplete for foreign keys, clickable links
- Data export — CSV/JSON export with Excel compatibility
- Dashboard widgets — stats cards, quick actions, recent activity
PRADA provides 3 abstraction levels. Use what fits your needs:
app.use('/admin', await createPradaServer({
prisma,
schemaPath: './prisma/schema', // single file or directory
modules: [statsModule, uploadsModule],
audit: true, // Enable change tracking
hooks: {
'*': { beforeCreate: async (data) => ({ ...data, createdAt: new Date() }) },
User: { beforeCreate: async (data) => ({ ...data, password: await hash(data.password) }) }
},
models: {
User: { actions: ['read', 'update'], fields: { password: { hidden: true } } }
}
}))import {
parseSchema, createApiHandler, createCrudRoutes,
createAuthService, createAuthMiddleware, createAuthRoutes
} from '@blysspeak/prada'
const schema = await parseSchema()
const api = createApiHandler(prisma, schema, { hooks })
const auth = createAuthService({ login: 'admin', password: 'secret' })
router.use('/api/auth', createAuthRoutes(auth))
router.use('/api', createAuthMiddleware(auth), createCrudRoutes(api))import {
buildWhereClause, buildOrderByClause, parsePagination,
generateToken, verifyToken, hashPassword,
getScalarFields, getIdField, getSearchableFields
} from '@blysspeak/prada'Extend the admin panel with custom backend logic:
import { Router } from 'express'
import type { PradaModule } from '@blysspeak/prada'
const statsModule: PradaModule = {
name: 'stats',
routes: async (ctx) => {
const router = Router()
router.get('/stats/overview', async (req, res) => {
const users = await ctx.prisma.user.count()
const orders = await ctx.prisma.order.count()
res.json({ users, orders })
})
ctx.router.use('/api', ctx.authMiddleware, router)
}
}
app.use('/admin', await createPradaServer({
prisma,
modules: [statsModule]
}))The module receives PradaContext with prisma, schema, router, authMiddleware, and config.
The entire UI is customizable through a single PradaConfig object:
import { App } from '@blysspeak/prada-ui'
<App config={{
// Custom form fields
fields: {
byType: { json: MonacoJsonEditor },
byName: { avatar: AvatarUpload },
byModelField: { User: { bio: RichTextEditor } }
},
// Custom table cells
cells: {
byName: { status: StatusBadge },
byModelField: { Order: { total: CurrencyCell } }
},
// Replace entire pages
pages: {
dashboard: CustomDashboard
},
// Insert components into pages
slots: {
listHeader: ({ model }) => <StatsBar model={model} />,
formFooter: ({ model }) => <AuditLog model={model} />
},
// Add custom routes (appear in sidebar)
routes: [
{ path: '/analytics', element: AnalyticsPage,
sidebar: { label: 'Analytics', icon: BarChart } }
],
// Configure sidebar
sidebar: {
hiddenModels: ['_prisma_migrations'],
modelLabels: { User: 'Team Members' },
logo: CustomLogo
},
// Configure table actions
actions: {
rowActions: { Order: [ExportButton, RefundButton] },
hideActions: { AuditLog: ['edit', 'delete'] }
}
}} />Field resolution priority: model+field > field name > field type > default component.
Build custom pages with easy database access:
import {
useModelList, useModelRecord,
useModelCreate, useModelUpdate, useModelDelete
} from '@blysspeak/prada-ui'
function OrdersPage() {
const { data, meta, isLoading } = useModelList('Order', {
sort: 'createdAt',
order: 'desc',
filters: { status: 'pending' },
page: 1,
limit: 50
})
const { mutate: updateOrder } = useModelUpdate('Order')
const { mutate: deleteOrder } = useModelDelete('Order')
return (
<div>
<h1>Pending Orders ({meta.total})</h1>
{data.map(order => (
<OrderCard
key={order.id}
order={order}
onApprove={() => updateOrder({ id: order.id, data: { status: 'approved' } })}
onDelete={() => deleteOrder(order.id)}
/>
))}
</div>
)
}| Method | Endpoint | Description |
|---|---|---|
GET |
/api/schema |
Schema metadata (models, fields, enums) |
GET |
/api/:model |
List records with pagination |
GET |
/api/:model/:id |
Get single record |
POST |
/api/:model |
Create record |
PUT |
/api/:model/:id |
Update record |
DELETE |
/api/:model/:id |
Delete record |
POST |
/api/auth/login |
Login |
POST |
/api/auth/logout |
Logout |
GET |
/api/auth/me |
Current user |
POST |
/api/auth/refresh |
Refresh token |
GET |
/api/stats |
Per-model record counts |
DELETE |
/api/:model/bulk |
Bulk delete records |
PUT |
/api/:model/bulk |
Bulk update records |
GET |
/api/audit |
Audit log entries |
GET |
/api/audit/:model |
Audit log for model |
GET |
/api/audit/:model/:id |
Audit log for record |
Query parameters: ?page=1&limit=20&sort=createdAt&order=desc&search=john&include=posts,comments
Supports operator filters: ?field__contains=value&field__gte=10
| Document | Description |
|---|---|
| Getting Started | Installation, basic usage, environment variables |
| Architecture | 3-level abstraction, project structure, data flow |
| UI Customization | PradaConfig, fields, cells, pages, slots, routes, sidebar |
| Backend Modules | PradaModule, PradaContext, custom routes |
| Data Hooks | useModelList, useModelRecord, useModelCreate, etc. |
| API Reference | All exports, REST endpoints, TypeScript types |
| Документ | Описание |
|---|---|
| Быстрый старт | Установка, базовое использование, переменные окружения |
| Архитектура | 3 уровня абстракции, структура проекта, поток данных |
| Кастомизация UI | PradaConfig, поля, ячейки, страницы, слоты, роуты, сайдбар |
| Модули бэкенда | PradaModule, PradaContext, кастомные роуты |
| Data Hooks | useModelList, useModelRecord, useModelCreate и др. |
| Справочник API | Все экспорты, REST-эндпоинты, TypeScript типы |
| Package | Description |
|---|---|
@blysspeak/prada |
Backend — Express middleware, schema parser, CRUD API, auth, module system |
@blysspeak/prada-ui |
Frontend — React components, data hooks, customization system |
- Node.js 18+
- PostgreSQL
- Prisma schema (
schema.prismaor directory of.prismafiles)
git clone https://github.com/blysspeak/prada.git
cd prada
pnpm install
pnpm build # Build all packages
pnpm dev # UI dev server with hot reloadpnpm build:main # Backend only
pnpm build:ui # UI onlyMIT
