QUESTPIE

Admin

The admin is a data workspace generated from your schema. The people who own the data get a real interface, instead of a database client or a spreadsheet that drifts.

View markdown

The admin is generic over your schema. It is not built for blog posts, or for products, or for any one kind of content. It reads what you declared and renders that. Add a field and it turns up in the form on the next generate. Nobody keeps a second copy of your model in a React app.

This group covers how far you can move it, and where the built-in behaviour stops.

Turn it on

The admin is a module with two halves. Register each one.

src/questpie/server/modules.ts
import { adminModule } from "@questpie/admin/modules/admin";

export default [adminModule] as const;
src/questpie/admin/modules.ts
import { adminClientModule } from "@questpie/admin/client/modules/admin";

export default [adminClientModule] as const;
questpie generate

The server half contributes collections, routes, views, components and two field types, f.richText() and f.blocks(). The client half turns the server's references into React screens. Generate writes src/questpie/admin/.generated/client.ts, and your /admin route mounts it. create-questpie writes all of this for you.

adminModule also pulls in starterModule. That is where the Better Auth tables and the assets upload collection come from. A fresh panel has users and a media library on day one.

Two runtimes, not four

The admin needs a render layer. create-questpie turns it on by default for TanStack Start and Next, and rejects it on Hono and Elysia. Those two stop at the API and the typed client.

What you get before you configure anything

URLWhat is there
/adminThe dashboard
/admin/collections/postsThe list, with filters, sorting and a column picker
/admin/collections/posts/createAn empty form
/admin/collections/posts/:idThat row's form
/admin/globals/siteSettingsThe one row a global holds
/admin/loginSign in
/admin/setupCreates the first admin, while none exists

The URL uses the registered name, not the file name. So collection("blog-posts") sits at /admin/collections/blogPosts.

A form with no form config generates its fields from the collection. A list with no list config picks its own columns. The title first, then up to six short fields, then createdAt. Relations, uploads, rich text, JSON, objects, arrays and textareas stay out of that default, because they are wide or expensive to load. Every one of them is still in the column picker.

Who may sign in

One rule decides it. session.user.role === "admin". A signed-in user without that role is not an admin user, and every admin RPC route refuses them.

Read access filters the rest. A collection your session cannot read is missing from the config the client receives, so it is missing from the sidebar too.

The first admin is a chicken-and-egg problem. /admin/login asks the server whether any user holds the admin role. If none does, it sends you to /admin/setup, which creates one. Once one exists, setup refuses to create any more.

Where each topic lives

TopicPage
Labels, icons, list columns, form layout, actionsCollections and globals
Sidebar, dashboard, branding, theming, mountingConfiguration
Your own views, widgets, pages and blocksCustom views
Better Auth, roles, setup and invitationsAuthentication
Who changed what, and whenAudit
Stacked content a person arrangesBlocks
How a module contributes all of the aboveModules

Next

Collections and globals is the page to read first. It covers .admin(), .list(), .form(), .preview() and .actions(), the five methods @questpie/admin adds to a collection.

On this page