ユースケース
MemGate の Lookup API を活用した具体的なサービス統合パターンを紹介します。
ログインゲート
最もシンプルなパターンです。Lookup API の結果が active ならログインを許可し、それ以外は拒否します。
フロー
- ユーザーがログインフォームで noteID とメールアドレスを入力
- サーバーが MemGate Lookup API を呼び出し
status: "active"→ ログイン成功(セッション発行)- それ以外 → ログイン拒否
typescript
// POST /api/login
app.post("/api/login", async (req, res) => {
const { note_id, email } = req.body;
const result = await lookupMember(note_id, email);
if (result.member?.status !== "active") {
return res.status(401).json({
error: "有効なメンバーシップが必要です",
});
}
// ログイン成功: セッションを発行
const session = await createSession({
noteId: note_id,
email,
plan: result.member.plan,
});
res.json({ session_token: session.token });
});ポイント
- セッションを発行して、毎回 API を呼び出さないようにする
- セッション有効期限をメンバーシップの更新頻度に合わせる
expiredとnot_foundを同じように拒否して OK
機能トグル
すべてのユーザーがログインできるが、メンバーシップのプラン文字列によって プレミアム機能の有効/無効を切り替えるパターンです。
フロー
- ユーザーが noteID とメールアドレスでログイン(全員許可)
- Lookup API でプラン情報を取得
planの値に応じて UI の機能を有効/無効に切り替え
typescript
// ログイン時にプラン情報をセッションに保存
app.post("/api/login", async (req, res) => {
const { note_id, email } = req.body;
const result = await lookupMember(note_id, email);
// メンバーシップの有無に関わらずログイン可能
const plan = result.member?.status === "active"
? result.member.plan
: null;
const session = await createSession({
noteId: note_id,
email,
plan, // "Proプラン", "Liteプラン", or null
isMember: plan !== null,
});
res.json({ session_token: session.token });
});
// 機能ごとのアクセスチェック
function canAccessPremiumFeature(session: Session): boolean {
return session.isMember && session.plan !== null;
}typescript
// フロントエンドでの条件分岐例
function Dashboard({ session }: { session: Session }) {
return (
<div>
<h1>ダッシュボード</h1>
{/* 全ユーザーに表示 */}
<BasicContent />
{/* メンバーのみに表示 */}
{session.isMember ? (
<PremiumContent plan={session.plan} />
) : (
<UpgradePrompt />
)}
</div>
);
}プランベースのアクセス制御
プラン文字列を権限レベルにマッピングし、 プランごとにアクセス可能な機能を制御するパターンです。
plan はメンバーシップ CSV の値がそのまま入ります
例: "Liteプラン", "Proプラン", "スタンダードプラン" など。 あなたのサービス側でプラン文字列を権限にマッピングしてください。
typescript
// プラン文字列 → 権限レベルのマッピング
// MemGate はプラン名を自由文字列として返すので、
// マッピングはあなたのサービス側で定義してください。
const PLAN_LEVELS: Record<string, number> = {
"Liteプラン": 1,
"スタンダードプラン": 2,
"Proプラン": 3,
};
function getPlanLevel(plan: string | null): number {
if (!plan) return 0; // 非メンバー
return PLAN_LEVELS[plan] ?? 1; // 未知のプランは最低レベル
}
// 機能ごとに必要なプランレベルを定義
const FEATURE_REQUIREMENTS: Record<string, number> = {
"basic-content": 0, // 全員アクセス可
"member-forum": 1, // Lite 以上
"advanced-analytics": 2, // スタンダード 以上
"priority-support": 3, // Pro のみ
};
function canAccess(
feature: string,
userPlan: string | null
): boolean {
const required = FEATURE_REQUIREMENTS[feature] ?? 0;
return getPlanLevel(userPlan) >= required;
}typescript
// ミドルウェアとして使用
function requirePlan(minLevel: number) {
return (req: Request, res: Response, next: NextFunction) => {
const level = getPlanLevel(req.session.plan);
if (level < minLevel) {
return res.status(403).json({
error: "この機能にはより上位のプランが必要です",
required_level: minLevel,
current_level: level,
});
}
next();
};
}
// ルーティング例
app.get("/api/analytics", requirePlan(2), analyticsHandler);
app.get("/api/support", requirePlan(3), supportHandler);推奨事項
- プランのマッピングは設定ファイルや環境変数で管理し、コードに直接書かない
- 未知のプラン文字列が返された場合のデフォルト動作を決めておく
- プラン情報はセッションに保存し、毎リクエストで API を呼ばない
- CSV の更新タイミング(月1回など)に合わせてセッションの再検証を行う