Express + MikroORM
Install paneljs, @paneljs/express, and @paneljs/mikroorm into your Express + MikroORM app and open /admin.
Requirements
- Node.js
^20.19 || ^22.12 || >=24or Bun>=1.3 - Express 4 or 5
@mikro-orm/core^6.4.0and the matching v6 driver for your database- An initialized
MikroORMinstance with your entities registered
PanelJS reads live MikroORM metadata. Call await MikroORM.init() before you pass the ORM instance to the adapter.
Install
CLI, from the root of this app (does not rewrite source):
npx paneljs@latest init --framework express --orm mikroorm --yesOr add the packages yourself:
$ npm install paneljs @paneljs/express @paneljs/mikroorm$ pnpm add paneljs @paneljs/express @paneljs/mikroorm$ yarn add paneljs @paneljs/express @paneljs/mikroorm$ bun add paneljs @paneljs/express @paneljs/mikroormExpress, MikroORM core, and your database driver are peers. For PostgreSQL:
$ npm install express @mikro-orm/core @mikro-orm/postgresql$ pnpm add express @mikro-orm/core @mikro-orm/postgresql$ yarn add express @mikro-orm/core @mikro-orm/postgresql$ bun add express @mikro-orm/core @mikro-orm/postgresqlUse the driver your app already needs (@mikro-orm/mysql, @mikro-orm/sqlite, and so on), and keep it on the same major as @mikro-orm/core.
Add it to your server
Initialize MikroORM first, then create the admin:
import express from "express";
import { MikroORM } from "@mikro-orm/postgresql";
import { createAdmin } from "paneljs";
import { mount } from "@paneljs/express";
import { mikroormAdapter } from "@paneljs/mikroorm";
import { entities } from "./entities.js";
const orm = await MikroORM.init({
clientUrl: process.env.DATABASE_URL,
entities,
});
const app = express();
const admin = createAdmin({
adapter: mikroormAdapter({ orm }),
siteName: "Admin",
auth: {
getCurrentUser: async (req) => {
const user = await getOperatorFromYourAuth(req);
if (!user) return null;
return {
id: user.id,
email: user.email,
role: user.role,
isSuperAdmin: user.role === "SUPER_ADMIN",
};
},
},
});
admin.register("User");
admin.register("Post");
await mount(app, admin);
app.listen(3000);Replace "User" / "Post" with MikroORM entity class names or EntitySchema names, PascalCase and exact. Table names do not change the register name.
Built-in login
Add PanelJS's auth entities to the same MikroORM configuration:
import { builtInAuthEntities, mikroormAdapter } from "@paneljs/mikroorm";
const orm = await MikroORM.init({
clientUrl: process.env.DATABASE_URL,
entities: [...appEntities, ...builtInAuthEntities({ identifier: "email" })],
});
const admin = createAdmin({
adapter: mikroormAdapter({ orm }),
auth: { mode: "built-in", identifier: "email" },
});Sync or migrate those entities with your normal MikroORM workflow. Do not register("ExpressAdminUser").
Create the first operator with npx paneljs createsuperuser --config ./paneljs.config.mjs. The config must export an initialized mikroormAdapter and the built-in auth settings. See Authentication.
Open the admin
Start your server and visit:
http://localhost:3000/adminWhat to do next
- Wire it into your app — lists, search, filters, scope, actions, and audit
- Authentication — built-in login or an existing session
- MikroORM adapter — metadata, writes, and
mikroormActionWhere - MikroORM notes — supported version and current limits