Skip to content

Express + MikroORM

Install paneljs, @paneljs/express, and @paneljs/mikroorm into your Express + MikroORM app and open /admin.

Requirements

  • Node.js ^20.19 || ^22.12 || >=24 or Bun >=1.3
  • Express 4 or 5
  • @mikro-orm/core ^6.4.0 and the matching v6 driver for your database
  • An initialized MikroORM instance 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):

sh
npx paneljs@latest init --framework express --orm mikroorm --yes

Or add the packages yourself:

sh
$ npm install paneljs @paneljs/express @paneljs/mikroorm
sh
$ pnpm add paneljs @paneljs/express @paneljs/mikroorm
sh
$ yarn add paneljs @paneljs/express @paneljs/mikroorm
sh
$ bun add paneljs @paneljs/express @paneljs/mikroorm

Express, MikroORM core, and your database driver are peers. For PostgreSQL:

sh
$ npm install express @mikro-orm/core @mikro-orm/postgresql
sh
$ pnpm add express @mikro-orm/core @mikro-orm/postgresql
sh
$ yarn add express @mikro-orm/core @mikro-orm/postgresql
sh
$ bun add express @mikro-orm/core @mikro-orm/postgresql

Use 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:

ts
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:

ts
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/admin

What to do next

Released under the MIT License.