Skip to content

What this is

PanelJS is an operations panel for a Node app that already has a data model.

You register the models you want to operate on. At mount, the library asks your data adapter what exists, builds metadata, and serves:

  • a React admin UI at /admin
  • a guarded JSON API at /admin/api/*

You do not describe your tables twice. Prisma’s schema.prisma, TypeORM entities, or MikroORM metadata are the source of truth. admin.register("User") with no extra config still produces a list, search, filters, and a create/edit form.

Two choices, then the same product

You pickPackageJob
HTTP framework@paneljs/express todaymount(app, admin)
ORM@paneljs/prisma, @paneljs/typeorm, or @paneljs/mikroormIntrospect models, run CRUD
CorepaneljsRegistry, schema JSON, UI, permissions, scope

Express is how the admin hangs off the server today. Prisma, TypeORM, and MikroORM are how rows are discovered and written. After mount, register("User") is the same.

Installation is the chooser for those two. The rest of this guide is shared.

The whole public API

ts
const admin = createAdmin({
  adapter: prismaAdapter({ prisma }), // or a TypeORM / MikroORM adapter
  auth: { getCurrentUser },
});

admin
  .register("User")
  .register("Post", { listDisplay: ["title", "author", "published"] });

await mount(app, admin);

Three calls. Everything else is optional configuration on those calls.

What you bring

You ownThe library owns
HTTP app (Express today)Admin routes under basePath
ORM client or initialized ORM instanceIntrospection through the adapter
Built-in admin credentials, or external authenticationCreating an AdminUser request context
Tenancy rules, via scope()Applying that scope on every record operation
Optional audit destinationEmitting safe, append-only events

Built-in mode provides an admin-only login screen, ExpressAdminUser table, and session store. External mode still lets you map an existing identity onto an AdminUser. See Authentication.

What this is not

  • Not a replacement for your public API
  • Not a CMS with nested document editing
  • Not AdminJS with an adapter bolted on — PanelJS metadata is the contract
  • Not a new project generator — it mounts on the app you already have

Current writes are scalar plus a single belongsTo foreign key. Nested creates, many-to-many editors, inlines, and uploads are not included.

The people in these docs

The Prisma, TypeORM, and MikroORM examples run the same tenant and operator story. The Trust and Extend guides reuse it so the rules stay concrete.

PersonRoleTenantWhat they see
Ada LovelaceADMINNorthwindNorthwind users and posts
Grace HopperADMINContosoContoso users and posts
Linus TorvaldsSUPER_ADMINNorthwindBoth, because scope returns {}

Same role does not mean same rows. That distinction — permissions vs scope — is the point of this library.

Next

  1. Installation — pick framework, then ORM, then mount
  2. Wire it into your applistDisplay, searchFields, listFilter, scope

Released under the MIT License.