本記事はQmonus Value Streamの投稿キャンペーン記事です。
はじめに
個人開発でアプリのコードを書くのはまあ良いとしても、管理のために必要なCRUD処理のコードを書くのは結構億劫だし、価値も生み出せているとは言えないのでやりたくない作業の1つだと思う。
そこで今回は、AdminJSを利用してサクッと管理画面の開発をしたいと思う。また、カスタムのコンポーネントを作成してそれを適用するという事もやってみようと思う。
AdminJSで管理画面をサクッと開発
今回はMySQLをデータベースとして利用しているアプリの管理画面を開発する。アプリの方ではORMを利用していないが、管理画面の開発ではPrismaを利用する(AdminJSのアダプターにPrisnaがあること、DDLから簡単にPrisma schemaを作成できること、の2点でそうすることにした)。
prisma schemaの作成
まずはprisma schemaを作成する。
npx prisma init
でprisma/schema.prisma
と.env
が作成される。.env
を以下のように書き換えてMySQLの接続情報を設定する。
DATABASE_URL="mysql://root:@localhost:3306/sample_db"
続いて現在のDDLに基づいて、prisma schemaを以下のコマンドで作成し、prisma clientを作成する。
$ npx prisma db pull
$ npx prisma generate
これでPrismaの事前準備は完了。
AdminJSをExpressサーバーに導入する
今回はNode.jsのExpressサーバーを管理画面のサーバーとすることにしたので、AdminJSのExpress用のプラグインを利用してAdminJSの設定をする。
import AdminJS from 'adminjs';
import AdminJSExpress from '@adminjs/express';
import express from 'express';
import { Database, Resource, getModelByName } from '@adminjs/prisma';
import { PrismaClient } from '@prisma/client';
const PORT = 3080;
const prisma = new PrismaClient();
AdminJS.registerAdapter({ Database, Resource });
const start = async () => {
const app = express();
const adminOptions = {
resources: [
{
resource: { model: getModelByName('users'), client: prisma },
options: {}
},
{
resource: { model: getModelByName('todos'), client: prisma },
options: {}
},
...
]
};
const admin = new AdminJS(adminOptions);
const adminRouter = AdminJSExpress.buildRouter(admin);
app.use(admin.options.rootPath, adminRouter);
app.listen(PORT, () => {
console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`);
});
};
await start();
上記のコードの内、adminOptions
の部分などはprismaのアダプター用の設定で、それ以外はexpressのプラグインの設定になる。詳細は以下の参考に書かれている通り。
ここまで設定すると、以下の画像のようにCRUDを行る管理画面が立ち上がるようになる。
あとは画面にある各種ボタンからCRUDを行って管理する感じになる。
ここまでAdminJSでの管理画面の開発は完了になる。続けて、カスタムコンポーネントとCSSスタイルの上書きをやってみたいと思う。
カスタムコンポーネントを適用する
AdminJSではReactコンポーネントを独自に実装することで、カスタムコンポーネントとして適用できる。
AdminJSのデフォルトの入力項目の実装は、HTMLのinput
タグで改行ができない。今回はこれをテキストアリアに変えることで改行も入力できるようにしてみる。
まずはReactのコンポーネントを実装する必要があるが、ReactがわからなくてもChatGPT等の生成AIに頼めば以下のようなコードは簡単に生成してくれるので、それを利用すればReactわからん!という状態でもなんとかなるだろう(管理画面の作りこみをしたいわけではないので)。
import React from 'react';
import { BasePropertyProps } from 'adminjs';
const TextareaEdit: React.FC<BasePropertyProps> = (props) => {
const { record, property, onChange } = props;
return (
<div className="mb-4">
<label className="block text-sm mb-2">{property.label}</label>
<textarea
className="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
rows={5}
value={record.params[property.path] || ''}
onChange={(e) => onChange(property.path, e.target.value)}
/>
</div>
);
};
export default TextareaEdit;
続いてこのコンポーネントをAdminJSに適用するための設定を実装するが、以下のようなComponentLoader
で読み込んだコンポーネントのオブジェクトを作成し、それをexportしておき、それを適用したいresource
のオプションで設定する。
import { ComponentLoader } from 'adminjs';
const componentLoader = new ComponentLoader();
const Components = {
TextareaEdit: componentLoader.add('TextareaEdit', './components/TextareaEdit')
};
export { componentLoader, Components };
import AdminJS from 'adminjs';
import AdminJSExpress from '@adminjs/express';
import express from 'express';
import { Database, Resource, getModelByName } from '@adminjs/prisma';
import { PrismaClient } from '@prisma/client';
import { Components, componentLoader } from './adminjs/components.js';
const PORT = 3080;
const prisma = new PrismaClient();
AdminJS.registerAdapter({ Database, Resource });
const start = async () => {
const app = express();
const adminOptions = {
resources: [
{
resource: { model: getModelByName('users'), client: prisma },
options: {
properties: {
name: {
type: 'string',
components: {
edit: Components.TextareaEdit, // <- ここに設定する
}
}
}
}
},
...
],
componentLoader,
};
const admin = new AdminJS(adminOptions);
const adminRouter = AdminJSExpress.buildRouter(admin);
app.use(admin.options.rootPath, adminRouter);
app.listen(PORT, () => {
console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`);
});
};
await start();
上記のような設定をすると、以下のようにカスタムコンポーネントが適用されて、input
ではなくtextarea
になることが確認できる。
上記はTailwind CSSが適用されてそのスタイルに装飾されているが、それについては次の項で見ていく。
Tailwind CSSを適用する
カスタムコンポーネントを適用するで見たように、適用したコンポーネントにはTailwind CSSのスタイルが適用されていた。これはAdminJSのassets
でスタイルを指定することで実現できる。
...
import * as url from 'url';
import path from 'path';
const PORT = 3000;
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const prisma = new PrismaClient();
AdminJS.registerAdapter({ Database, Resource });
const start = async () => {
const app = express();
const adminOptions = {
resources: [
{
...
},
...
],
componentLoader,
assets: {
styles: ['/tailwind.css']
}
};
const admin = new AdminJS(adminOptions);
const adminRouter = AdminJSExpress.buildRouter(admin);
app.use(admin.options.rootPath, adminRouter);
app.use(express.static(path.join(__dirname, '../public')));
app.listen(PORT, () => {
console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`);
});
};
await start();
実装としては、上記のようにadminOptions
のassets
の設定を行い、assets.styles
で指定したCSSファイルがExpressのstaticの機能で配信されるようにすればよい。今回は./public
ディレクトリをstaticの対象にしているので、./public/tailwind.css
というCSSがAdminJSのスタイルとして追加で設定されるようになる。
ところで./public/tailwind.css
を作成する方法だが、Get started with Tailwind CSSに書かれている方法を利用する。
つまり、content
にTailwind CSSを利用しているファイルのパスを指定して、
$ npx tailwindcss -i ./src/styles/tailwind.css -o ./public/tailwind.css --watch
というコマンドを実行すれば作成できる。
export default {
content: [
'./src/adminjs/components/**/*.tsx'
],
theme: {
extend: {}
},
plugins: []
};
@tailwind base;
@tailwind components;
@tailwind utilities;
まとめとして
今回はAdminJSを利用してサクッと管理画面を開発する方法を見てきた。また、カスタムコンポーネントを適用し、カスタムコンポーネントを実装する際にTailwind CSSを利用できるようにする方法もみてきた。
ほんの30分程度で管理画面を作成してやりたいことができるようになったので、管理画面が必要になった時には今後もAdminJSを利用していきたいと思った。