SaaSの雛形は「認証・課金・テナント・ダッシュボード」だけでいい
SaaSの土台は最初に何を作り何を後回しにするかで決まる。最小4点セットの構成、既製ボイラープレートと自作の判断、Claude Codeで土台を素早く組む手順を僕の失敗込みで。
週末に新しいSaaSのアイデアを思いついて、僕は意気込んでこう打ち込みました。「Next.jsでSaaSを作って。認証も課金もチーム機能もダッシュボードも全部入りで」。
Claude Codeは素直に従いました。30分後、画面はちゃんと動いていた。ログインもできた。ダッシュボードにグラフも出ていた。なのに——別のチームのURLを手で打ったら、よその会社のデータが普通に見えたんです。
雛形(ボイラープレート)づくりで一番やりがちな事故が、これです。「動く土台」と「壊れない土台」はまったく別物。今日は、SaaSの土台を何から作り、何を後回しにするかという順番の話を、僕がつまずいた順に書いていきます。
この記事の要点
- SaaSの最小の土台は認証・課金・マルチテナント・ダッシュボードの4点だけ。これ以外は最初は全部後回しでいい。
- 4つの中でも、テナント境界(データの仕切り)を先に決めるのが最優先。後付けは事故るし、書き直しが一番痛い。
- 既製ボイラープレートか自作かは「学びたいか/急ぎたいか」で割り切る。
stripe課金やOAuthログインは既製、ドメイン固有のロジックは自作が基本線。 - Claude Codeには「いきなり全部」ではなく、
CLAUDE.mdに境界を書いてからスキーマ→ガード関数→画面の順で頼むと差分が小さく、レビューが効く。 - 各要素(認証/RBAC/決済/ダッシュボード)の深掘りは個別記事に分けた。この記事は全体の地図。
SaaSの土台は、実はたった4つ
「SaaSに必要な機能」をリストアップし始めると、止まらなくなります。通知、招待、Webhook、利用量メーター、Slack連携、SSO、監査ログ、管理画面……。全部いる気がしてくる。
でも最初に必要なのは、たった4つです。
| 要素 | 一言でいうと | これが無いと |
|---|---|---|
| 認証 | 誰がログインしているか | そもそもユーザーを識別できない |
| マルチテナント | 会社・チームごとにデータを仕切る | よそのデータが見える事故 |
| 課金 | お金を受け取る仕組み | 売上が立たない |
| ダッシュボード | ログイン後の最初の画面 | ユーザーが何をすればいいか分からない |
逆に言うと、招待・通知・SSO・利用量課金・管理画面・監査ログは、全部この4つの後でいい。最初のユーザーが1人つくまで、招待機能は1行も書かなくていいんです。僕は昔これを逆にやって、誰も使っていない招待フローのデバッグに週末を溶かしました。
「マルチテナント」だけ補足します。1つのアプリを複数の会社で安全に共有する仕組みのことです。賃貸マンションを想像してください。建物(アプリ)は共通でも、201号室の住人が301号室に勝手に入れたら大問題。その「鍵」を設計するのがマルチテナント設計です。
何を先に作り、何を後回しにするか
順番には正解があります。後から差し込むと一番痛い順、つまり土台に近い順に作る。
- テナントの仕切り(最優先)。全データに「どのテナントのものか」という印を付ける。これを後付けすると、既存の全テーブルとクエリを直す羽目になります。
- 認証。ログインとセッション。テナントに人を結びつける土台。
- 権限(ロール)。オーナー・管理者・請求担当・一般。最初は2〜3種類で十分。
- 課金。プラン選択とStripe決済。サブスクの状態をDBに持つ。
- ダッシュボード。ここでやっと「画面」。上の4つが固まっていれば、画面は一番気楽な作業です。
後回しでいいもの、というより「ユーザーが増えてから困ったら作る」もの。
- 招待・メンバー管理(ユーザーが2人目を呼びたくなってから)
- 利用量に応じた従量課金(固定プランで売れてから)
- SSO・SAML(企業の引き合いが来てから)
- 凝った管理画面(
prisma studioやDB直見で当面しのげる) - 監査ログ(ただし課金とロール変更だけは最初から1行残す。後述)
この順番のキモは1番です。テナント境界の設計はマルチテナントSaaSを安全に実装する実務ガイドに独立した記事があるので、土台を本気で組むときは先にそちらを読んでください。ここでは「最初に決める」とだけ覚えてもらえれば十分です。
既製ボイラープレートか、自作か
ここで多くの人が悩みます。世の中には完成度の高い有料SaaSテンプレートがいくつもある。一方で「全部自分で書きたい」気持ちもある。
判断はシンプルで、**「これを自分で書くことから学びたいか?」**の一点です。
| 部分 | おすすめ | 理由 |
|---|---|---|
| OAuthログイン・セッション | 既製ライブラリ(Auth.js等) | 自作の認証は事故の温床。車輪の再発明で得るものが少ない |
| Stripe決済・Webhook | 公式SDK+薄い自前ラッパ | 決済ロジックは枯れたものを使う。ただし冪等性だけは自分で握る |
| テナント境界・権限チェック | 自作 | ここがプロダクトの心臓。他人のコードをコピペすると穴に気づけない |
| ドメイン固有の機能 | 自作 | そもそも誰も書いてくれない。ここが価値の源泉 |
| UIコンポーネント | 既製(shadcn/ui等)+カスタム | 見た目で消耗しない。中身に集中する |
僕の結論は「認証と決済は他人の肩に乗る。テナントと権限は自分の手で書く」です。前者はバグると恥ずかしいだけだけど、後者はバグると会社が飛ぶ。リスクの重さが違うんですね。
既製テンプレートを買う場合の注意点も一つ。テンプレートのStripe Webhookが古いAPIで書かれていることがあります。2025年3月のStripe Basilでcurrent_period_endがサブスクリプション直下から各アイテムへ移動しました。古い雛形をそのまま使うと、更新日がundefinedになって静かに壊れます。買ったコードも疑う。これ大事です。
Claude Codeに渡すCLAUDE.md
冒頭の「全部入りで作って」が事故ったのは、Claude Codeが悪いんじゃありません。境界を伝えなかった僕が悪い。新人に「いい感じにSaaS作っといて」と丸投げするのと同じです。
そこでCLAUDE.mdに「越えてはいけない線」を先に書きます。これがエージェントの足場(ハーネス)になります。
# CLAUDE.md
## 作るもの
再利用できる有料SaaSの土台。最小構成は 認証 / テナント / 課金 / ダッシュボード。
この土台は法務・税務・セキュリティレビューの代わりにはならない。
## 技術スタック
- Next.js App Router + TypeScript
- Prisma + PostgreSQL
- Auth.js(OAuth/セッション)
- Stripe Checkout + Webhook
- Vitest / Playwright(受け入れテスト)
## 越えてはいけない線(最優先)
- すべての業務データは tenantId を持つ。
- ブラウザから来た tenantId を、メンバーシップ確認なしに信用しない。
- ロールは OWNER / ADMIN / BILLING / MEMBER の4つ。
- 課金系ルートは OWNER か BILLING のみ。
- 課金とロール変更は必ず監査ログを1行残す。
- 秘密情報は src/lib/env.ts 経由で読む。ハードコード禁止。
- 「別テナントにアクセスできない」テストを必ず書く。
## 作る順番
1. Prisma スキーマ(テナント境界を先に)
2. テナント/ロールのガード関数
3. 認証 → 課金 → ダッシュボードの順で画面
## 出力ルール
編集後は、変更ファイル・実行コマンド・残るリスク・人間が確認すべき点を列挙する。
これを置いてから頼むと、Claude Codeが勝手にガード関数とテストを書く方向に寄ります。初心者ほど効きます。「テナントIDをブラウザから信用するな」の一文があるだけで、冒頭の「よそのデータが見える」事故が消えるんです。
テナント境界は、コードで強制する
土台の心臓部だけ、実際に動くコードを置きます。ポイントは一つだけ。「ブラウザから来たtenantIdを絶対に信じない」。URLにもフォームにも書けるし、いくらでも書き換えられるからです。
次の関数は、ログイン中のユーザーがそのテナントに本当に所属しているかをDBで確認し、足りない権限なら止めます。Next.js App Router+Prisma+Auth.jsを想定しています。
// src/lib/tenant.ts
import { Role } from "@prisma/client";
import { redirect } from "next/navigation";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
// ロールの強さ。数字が大きいほど強い権限。
const roleRank: Record<Role, number> = {
MEMBER: 1,
BILLING: 2,
ADMIN: 3,
OWNER: 4,
};
/**
* 門番。指定テナントに所属し、必要な権限を満たすときだけ通す。
* ブラウザから来た tenantId は、ここで必ず DB と突き合わせる。
*/
export async function requireTenant(tenantId: string, minimumRole: Role = "MEMBER") {
const session = await auth();
if (!session?.user?.id) redirect("/login"); // 未ログインは弾く
// ユーザー × テナントの所属を DB で確認(ここが肝)
const membership = await prisma.membership.findUnique({
where: {
userId_tenantId: { userId: session.user.id, tenantId },
},
include: { tenant: true },
});
// 所属していない、または権限が足りなければ止める
if (!membership || roleRank[membership.role] < roleRank[minimumRole]) {
throw new Error("このテナントへのアクセス権がありません");
}
return { userId: session.user.id, tenant: membership.tenant, role: membership.role };
}
使い方はこうです。課金ページなら BILLING 以上を要求します。
// src/app/dashboard/[tenantId]/billing/page.tsx
import { requireTenant } from "@/lib/tenant";
export default async function BillingPage({
params,
}: {
params: Promise<{ tenantId: string }>;
}) {
const { tenantId } = await params;
// 権限が足りなければ、この行で例外が飛んで以降は実行されない
const { tenant, role } = await requireTenant(tenantId, "BILLING");
return <h1>{tenant.name} の請求設定(あなたの権限: {role})</h1>;
}
このrequireTenantを全ページ・全API冒頭で呼ぶ。たったこれだけで土台の安全性が一段上がります。権限設計を細かく詰めたい人はRBACの実装記事へ、ログインそのものの作り込みはWeb認証をセッションで固める記事へ。この記事はあくまで地図なので、各論はリンク先に任せます。
ダッシュボードは最後でいい理由
「まずダッシュボードから作りたい」気持ち、分かります。目に見えて達成感があるから。でも順番として最後でいい。理由は、ダッシュボードは上の3つ(認証・テナント・権限)が揃って初めて意味を持つ画面だからです。
逆に言えば、土台ができていればダッシュボードは一番ラクな作業になります。requireTenantでテナントを取って、そのテナントのデータだけ表示すればいい。グラフ選びやレイアウトに悩むのはその後で十分です。チャート選定やURL同期みたいな実装の細部はダッシュボード開発の記事にまとめてあります。
最初のダッシュボードは「ようこそ+今日の数字3つ+次にやることへのボタン」くらいで十分。凝るのは、ユーザーが「もっと見たい」と言ってからです。
僕がやらかした失敗3つ
正直に書きます。最初に組んだSaaSの土台は、穴だらけでした。
ひとつ目は、テナント境界を後回しにしたこと。 「とりあえず動かして、仕切りは後で」とやったら、後で全テーブルにtenantIdを足し、全クエリを直す羽目になりました。半日仕事です。境界は最初に1回。これだけは譲れません。
ふたつ目は、課金を本物のお金が動く所までサンプルのまま進めたこと。 Stripe Webhookを「一度しか来ない前提」で書いていたら、再送イベントで二重に処理されかけました。Webhookは同じ通知が複数回来るのが普通です。冪等性(同じ処理を何度やっても結果が変わらない性質)を最初から入れる。この痛い話はStripeサブスクの冪等性の記事に詳しく書きました。
みっつ目は、監査ログを「後で全部入れよう」としたこと。 全操作にログを入れようとすると重くて挫折します。今は割り切って、課金の変化とロール変更の2つだけ最初から1行残す。「誰がいつプランを変えたか」「誰を管理者にしたか」さえ追えれば、初期トラブルの9割は調査できます。
よくある質問
Q. 既製のSaaSボイラープレートを買えば、自分で土台を作らなくていい?
土台の8割は省けますが、テナント境界と権限チェックだけは中身を理解しておくべきです。ここがバグると情報漏洩に直結します。買ったコードでもrequireTenant相当の処理は自分の目で追ってください。
Q. 最小構成だけで本当にリリースしていい? 個人向けマイクロSaaSなら十分です。認証+固定プラン課金+テナント+ダッシュボードがあれば売れます。招待やSSOは、ユーザーが実際に欲しがってから足すほうが、無駄な実装が減ります。
Q. マルチテナントは「テナントごとにDB」と「1つのDBでtenantIdで仕切る」どっちがいい?
最初は後者(1つのDB+tenantId列)で十分です。シンプルで運用が軽い。テナントごとにDBを分ける方式は、大企業向けのデータ分離要件が出てきてから検討すれば間に合います。
Q. Claude Codeに「SaaS作って」と一発で頼んでいい?
おすすめしません。CLAUDE.mdに境界を書いてから、スキーマ→ガード関数→画面の順に小さく頼むほうが、差分が読めてレビューが効きます。一発生成は動くけど穴に気づけません。
Q. 認証は自作とライブラリどっちがいい? ライブラリ一択です。Auth.jsのような枯れた実装に乗る。自作認証はパスワードハッシュ・セッション・CSRF対策など落とし穴が多すぎて、学習目的以外で書く理由がありません。
まとめ:土台は薄く、境界は厚く
SaaSの雛形づくりでブレないコツは、機能を足し算で考えないことです。
最小は認証・テナント・課金・ダッシュボードの4点。その中でもテナント境界を最初に、コードで強制する。認証と決済は他人の肩に乗り、テナントと権限は自分の手で書く。Claude CodeにはCLAUDE.mdで越えてはいけない線を渡してから、小さく順番に頼む。
「動く土台」を作るのは1日でできます。でも「壊れない土台」は、何を先に作るかの順番で決まる。冒頭の僕みたいに、よそのデータが見える画面を量産しないために——土台は薄く、境界は厚く。ここから始めてください。
この4点をそのまま自分のリポジトリへ落とし込みたいなら、CLAUDE.md雛形やテスト観点をまとめたClaude Codeの教材・テンプレートも用意しています。チームで一気に導入したい場合は研修・導入相談へどうぞ。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
制作会社がClaude Codeに触らせる前に決める権限チェックリスト
クライアントサイトを壊さずにAI編集を使うための、制作会社向け権限と確認の型です。
SaaSサポートのバグ報告をClaude Codeで再現手順に変える実務フロー
問い合わせ文をそのまま開発へ投げず、再現手順、証拠、次の一手に整えるサポート向け手順です。
Obsidianの古いメモをClaude Codeの指示書に変える10分ルーチン
Obsidianに溜めたメモが毎回ゴミになる人へ。事実・決定・未確認に仕分けして、Claude Codeがそのまま動ける指示書に変える朝の10分の型を紹介します。