Skip to content

Express + TypeORM ​

Install paneljs, @paneljs/express, and @paneljs/typeorm into your Express + TypeORM app and open /admin.

Requirements ​

  • Node.js ^20.19 || ^22.12 || >=24 or Bun >=1.3
  • Express 4 or 5
  • typeorm ^0.3.20
  • An initialized TypeORM DataSource with your entities registered

Introspection reads live entity metadata. The DataSource must already be initialize()d before you pass it to the adapter.

Install ​

CLI, from the root of this app (does not rewrite source):

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

Or add the packages yourself:

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

Express and TypeORM are peers. If this app does not have them yet:

sh
$ npm install express typeorm
sh
$ pnpm add express typeorm
sh
$ yarn add express typeorm
sh
$ bun add express typeorm

Add it to your server ​

Initialize the DataSource first, then create the admin:

ts
import express from "express";
import { createAdmin } from "paneljs";
import { mount } from "@paneljs/express";
import { typeormAdapter } from "@paneljs/typeorm";
import { dataSource } from "./data-source.js";

await dataSource.initialize();

const app = express();

const admin = createAdmin({
  adapter: typeormAdapter({ dataSource }),
  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 entity names registered on the DataSource (@Entity() class name or EntitySchema name), PascalCase, exact.

typeormAdapter throws if the DataSource is not initialized.

Built-in login ​

If you want /admin/login instead of getCurrentUser, add the auth entities to the DataSource and switch auth mode:

ts
import { builtInAuthEntities, typeormAdapter } from "@paneljs/typeorm";

const dataSource = new DataSource({
  type: "postgres",
  url: process.env.DATABASE_URL,
  entities: [...appEntities, ...builtInAuthEntities({ identifier: "email" })],
});
await dataSource.initialize();

const admin = createAdmin({
  adapter: typeormAdapter({ dataSource }),
  auth: { mode: "built-in", identifier: "email" },
});

Do not register("ExpressAdminUser"). See Authentication.

Create the first operator with npx paneljs createsuperuser --config ./paneljs.config.mjs. The config must export an initialized typeormAdapter and the built-in auth settings. See Authentication.

Open the admin ​

Start your server and visit:

http://localhost:3000/admin

You should see a sidebar with every entity you registered. register("User") with no second argument is enough for a list and a form. Defaults come from entity metadata.

What to do next ​

Released under the MIT License.