この記事について
Web Dev Simplified の動画 How To Handle Permissions Like A Senior Dev を見て、フロントエンドでの権限制御分岐についての案を記載しました。
よくある表示制御:ロールで分岐する
// ❌ ロールで直接分岐している
{["admin", "editor", "writer"].includes(user.role) && <button>編集</button>}
{["admin", "editor"].includes(user.role) && <button>公開</button>}
{["admin", "editor"].includes(user.role) && <button>コメント削除</button>}
{user.role === "admin" && <a href="/admin">管理ページ</a>}
これだと、ロールが増えるたびにフロント側の条件が増えていきます。
-
reviewerのようなロールを足すと、編集・公開・コメント削除…と条件式を一つずつ直して回ることになります。
機能ベースにする
バックエンド:ロールごとに使える機能を定義
type Role = "admin" | "editor" | "writer" | "viewer";
type Feature = "articleEdit" | "articlePublish" | "commentDelete" | "adminPageView";
type AvailableFeature = Record<Feature, boolean>;
// ここを見れば、どのロールがどの機能を使えるか一目でわかる
const roleFeatures: Record<Role, AvailableFeature> = {
admin: { articleEdit: true, articlePublish: true, commentDelete: true, adminPageView: true },
editor: { articleEdit: true, articlePublish: true, commentDelete: true, adminPageView: false },
writer: { articleEdit: true, articlePublish: false, commentDelete: false, adminPageView: false },
viewer: { articleEdit: false, articlePublish: false, commentDelete: false, adminPageView: false },
};
// そのロールで利用できる機能を返す
res.json({ availableFeature: roleFeatures[user.role] });
フロントエンド:ロールではなく機能で分岐
// ✅ ロールが増えても、この分岐はこのまま変わらない
function ArticleActions({ availableFeature }: { availableFeature: AvailableFeature }) {
return (
<>
{availableFeature.articleEdit && <button>編集</button>}
{availableFeature.articlePublish && <button>公開</button>}
{availableFeature.commentDelete && <button>コメント削除</button>}
{availableFeature.adminPageView && <a href="/admin">管理ページ</a>}
</>
);
}
ロールを追加するときに直すのはバックエンドの roleFeatures に1行足すだけで、フロントのコードには手を入れません。roleFeatures を見ればどのロールが何をできるかもわかるので、権限まわりの確認も楽になります。
まとめ
- ロールと機能の対応はバックエンドの
roleFeaturesで持ち、API ではavailableFeatureを返す。 - フロントでロールを見て分岐すると、ロールが増えるたびに条件が増える。
- フロントは
availableFeatureを見るだけにすれば、ロールが増えてもコードは変わらない。 - 特にロールが多いアプリだとこちらの方が、フロント側の差分を抑えやすい