Declare collections with fields, access rules and hooks. Quickben gives you a typed server API, REST routes and an admin UI on your own database. Built for the Next.js App Router.
import { defineCollection, defineConfig, devAuth, fields as f } from "@quickben/core";
import { drizzleAdapter } from "@quickben/db-drizzle";
import { tenancy } from "@quickben/plugin-tenancy";
const users = defineCollection({
slug: "users",
fields: { email: f.email({ required: true, unique: true }) },
});
const posts = defineCollection({
slug: "posts",
labels: { singular: "Post", plural: "Posts" },
fields: {
title: f.text({ required: true, maxLength: 200 }),
status: f.select({ options: ["draft", "published"], default: "draft" }),
author: f.relation({ to: "users" }),
publishedAt: f.date(),
meta: f.json(),
},
access: {
read: () => true,
create: ({ user }) => !!user,
update: ({ user }) => (user ? { author: { eq: user.id } } : false),
delete: ({ user }) => user?.roles?.includes("admin") === true,
},
hooks: { beforeChange: [({ data }) => data] },
admin: { useAsTitle: "title", defaultColumns: ["title", "status", "publishedAt"] },
});
export default defineConfig({
db: drizzleAdapter({ dialect: "postgres", url: process.env.DATABASE_URL }),
collections: [users, posts],
plugins: [tenancy({ scope: ["posts"] })],
auth: devAuth(),
});Document, create, update and filter types come from the field definitions. No code generation.
type Post = InferDoc<typeof posts>;A rule returns true, false or a where filter. Reads AND the filter with the query. Writes must match it.
update: ({ user }) => ({ author: { eq: user.id } })One small adapter interface with a portable query model. In-memory for tests. Drizzle for Postgres, PGlite and SQLite, with drizzle-kit migrations.
db: drizzleAdapter({ dialect: "pglite", dataDir: "./.pglite" })A catch-all route handler for REST and a catch-all page for the admin. The admin page also defines the server function for saves.
app/api/[...quickben]/route.ts
app/admin/[[...segments]]/page.tsxDashboard, list, create and edit screens from your fields. The sidebar comes from collections and modules. No Tailwind setup in your app.
admin: { useAsTitle: "title" }A plugin adds collections, hooks and modules. The tenancy plugin adds organizations, memberships and scoped access.
plugins: [tenancy({ scope: ["posts"] })]

Screens from the playground. Same config, same access rules as the REST API.
Collections, fields, access rules and hooks in quickben.config.ts.
defineConfig({ db, collections })Typed reads and writes on the server. Access and hooks run on every call.
await qb.find("posts", { where }, { user })Route handlers and admin pages call the same local API.
createRouteHandlers(config)
createAdminPage(config, { serverFunction })Quickben is alpha. APIs can change before 1.0. The code is open on GitHub under Apache-2.0, built and maintained by Vialabs.
| Package | Status |
|---|---|
@quickben/core | Available |
@quickben/db-memory | Available |
@quickben/db-drizzle | Available |
@quickben/next | Available |
@quickben/admin | Available |
@quickben/plugin-tenancy | Planned |