team-saas について
team-saasは、チームやワークスペース、メンバー招待を備えたマルチテナントSaaSの構築向けアーキテクチャスキルです。認証、バックグラウンドジョブ、デプロイ設定を含むNext.jsの設計パターンとテンプレートを提供します。
主な機能
- 01マルチテナントのチームとメンバー権限
- 02認証とチーム招待フロー
- 03APIルートの権限パターン
- 04バックグラウンドジョブキュー設定
- 05UIテーマとコンポーネントテンプレート
- 06DockerとRailwayのデプロイ設定
活用シーン
- 01チームワークスペース付きSaaSの立ち上げ
- 02チーム招待と認可の実装
- 03Next.jsアプリへのバックグラウンドジョブ追加
- 04Railwayデプロイ設定の準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ blink-new/ claude --skill team-saasインストール後、Claude Code で/team-saasを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/api/accept-invitation-route.tsinvitations-route.tsteams-route.tsuploads-proxy-route.tsassets/components/auth-layout.tsxdashboard-layout.tsxproviders.tsxroot-layout.tsxteam-switcher.tsxtheme-toggle.tsxassets/config/components.jsonDockerfileglobals.cssnext.config.tsprisma.config.tsproxy.tsrailway.tomltsconfig-paths.jsonassets/hooks/use-teams.tsassets/lib/api-helpers.tsauth.config.tsauth.tsboss.tsprisma.tsquery-client.tsresend.tss3.tsutils.tsassets/prisma/schema.prismaassets/types/next-auth.d.ts詳細
チーム向け SaaS アーキテクチャスキル
チームワークスペース、メンバー招待、認証、モダンな UI を備えた、本番環境に対応するマルチテナント SaaS アプリケーションを構築します。実際の本番アプリケーションを支える、実績あるアーキテクチャに基づいています。
このスキルを使用する場面
次の場合に使用します:
- チーム/ワークスペース機能を備えた新しい SaaS プロダクトを構築する
- マルチテナントの認証と認可を設定する
- チームメンバーの招待フローを実装する
- ライト/ダークモードを備えた Linear/Vercel スタイルのモダンな UI を作成する
- SaaS 向けのバックグラウンドジョブ処理を設定する
- フルスタックの Next.js アプリを Railway にデプロイする
技術スタックの概要
| レイヤー | テクノロジー | バージョン |
|---|---|---|
| フレームワーク | Next.js (App Router) | 16.x |
| React | React | 19.x |
| ランタイム | Bun | Latest |
| データベース | PostgreSQL (Railway) | - |
| ORM | Prisma | 7.x |
| 認証 | NextAuth v5 (Auth.js) | 5.0.0-beta |
| UI | shadcn/ui (new-york) | Latest |
| スタイリング | Tailwind CSS | v4 |
| テーマ設定 | next-themes | Latest |
| アイコン | Lucide React | Latest |
| 状態管理 | TanStack React Query | 5.x |
| バリデーション | Zod | 4.x |
| メール | Resend | 6.x |
| ストレージ | Railway S3-compatible | - |
| ジョブ | pg-boss | 12.x |
| ホスティング | Railway | - |
アーキテクチャ図
┌─────────────────────────────────────────────────────────────────────┐
│ Next.js Application │
├─────────────────────────────────────────────────────────────────────┤
│ Landing Page │ Auth Pages │ Dashboard (Protected) │
│ / │ /login │ /teams/[teamId]/* │
│ /pricing │ /register │ /dashboard │
│ │ /invite/[t] │ /settings │
├─────────────────────────────────────────────────────────────────────┤
│ API Routes │
│ /api/auth/* │ /api/teams/* │ /api/uploads/* │
│ /api/cron/* │ /api/webhooks/* │ /api/invitations/* │
├─────────────────────────────────────────────────────────────────────┤
│ proxy.ts (Security Headers) │ layout.tsx (Auth Protection) │
├─────────────────────────────────────────────────────────────────────┤
│ Services Layer │
│ Prisma (DB) │ Resend (Email) │ S3 (Storage) │ pg-boss (Jobs) │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Railway Infrastructure │
│ PostgreSQL │ S3 Bucket │ Redis (optional) │
└─────────────────────────────────────────────────────────────────────┘
中核となるパターン
1. マルチテナントのチーム構造
すべてのデータエンティティはチームに属します。ユーザーは複数のチームに、ロールを持つメンバーとして参加できます。
User ─┬─> TeamMember ─> Team ─┬─> Creator
│ ├─> Campaign
└─> TeamMember ─> Team ├─> Content
└─> ... (all team resources)
2. ルートグループ
src/app/
├── (auth)/ # Public auth pages (login, register)
│ └── layout.tsx # Client component, styling only
├── (dashboard)/ # Protected authenticated routes
│ ├── layout.tsx # Server component with auth() check + redirect
│ └── teams/[teamId]/ # Team-scoped pages
├── (marketing)/ # Public marketing pages
├── invite/[token]/ # Team invitation acceptance
└── api/ # API routes
3. ルート保護パターン(レイアウトベース。ミドルウェアは使用しません)
重要: 認証保護はサーバーコンポーネントのレイアウトチェックで行い、Edge ミドルウェアでは行いません。
// src/app/(dashboard)/layout.tsx
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
export default async function DashboardLayout({ children }) {
const session = await auth();
if (!session?.user) {
redirect("/login");
}
return <DashboardShell session={session}>{children}</DashboardShell>;
}
4. API ルートの権限パターン
// Use api-helpers for clean, consistent patterns
import { requireTeamMember, parseJsonBody, withErrorHandler, ApiError } from "@/lib/api-helpers";
type RouteParams = {
params: Promise<{ teamId: string }>; // Next.js 15+ async params
};
export const POST = withErrorHandler(async (req: NextRequest, { params }: RouteParams) => {
const { teamId } = await params; // MUST await params
const { userId, role } = await requireTeamMember(teamId); // Throws 401/403
if (role !== "ADMIN") {
throw new ApiError("Admin access required", 403);
}
const data = await parseJsonBody(req, mySchema); // Validates with Zod
// ... business logic
return NextResponse.json(result);
});
ファイル構成
src/
├── app/
│ ├── (auth)/ # Auth pages (public)
│ │ ├── login/page.tsx
│ │ ├── register/page.tsx
│ │ └── layout.tsx # Client component, no auth
│ ├── (dashboard)/ # Protected pages
│ │ ├── layout.tsx # Server component, auth check
│ │ ├── dashboard-shell.tsx # Sidebar + nav
│ │ └── teams/[teamId]/
│ ├── invite/[token]/page.tsx # Invitation acceptance
│ ├── api/
│ │ ├── auth/[...nextauth]/route.ts
│ │ ├── teams/
│ │ │ ├── route.ts # List/create teams
│ │ │ └── [teamId]/
│ │ │ ├── route.ts # Team CRUD
│ │ │ ├── members/
│ │ │ └── invitations/
│ │ ├── invitations/
│ │ │ └── [token]/accept/route.ts
│ │ └── uploads/[...path]/route.ts # S3 proxy
│ ├── globals.css # Design system
│ ├── layout.tsx # Root layout with Providers
│ └── page.tsx # Landing page
├── components/
│ ├── ui/ # shadcn components
│ ├── teams/ # Team management
│ ├── invitations/ # Invitation UI
│ ├── shared/ # Reusable patterns
│ ├── providers.tsx # App providers
│ └── theme-toggle.tsx # Theme switcher
├── hooks/
│ ├── use-teams.ts # Team hooks
│ └── ...
├── lib/
│ ├── auth.ts # NextAuth config (Node.js runtime)
│ ├── auth.config.ts # Auth config (callbacks, pages)
│ ├── api-helpers.ts # withErrorHandler, requireTeamMember, etc.
│ ├── prisma.ts # Prisma client (lazy proxy)
│ ├── s3.ts # S3 utilities
│ ├── resend.ts # Email client
│ ├── query-client.ts # React Query
│ ├── utils.ts # cn(), getInitials(), etc.
│ └── jobs/ # pg-boss setup
├── proxy.ts # Security headers (NOT auth)
├── types/
│ └── next-auth.d.ts # Auth type extensions
└── generated/
└── prisma/ # Prisma client output
関連アセットファイル
このスキルには、そのまま使えるテンプレートが含まれています:
| アセット | 説明 |
|---|---|
assets/lib/auth.ts | Credentials プロバイダーを使用する NextAuth v5 |
assets/lib/auth.config.ts | 認証設定(コールバック、カスタムページ) |
assets/lib/api-helpers.ts | withErrorHandler、requireTeamMember、ApiError |
assets/lib/prisma.ts | pg アダプターを使用する遅延ロードの Prisma クライアント |
assets/lib/s3.ts | 事前署名 URL を扱う S3 ユーティリティ |
assets/lib/resend.ts | Resend メールクライアントとテンプレート |
assets/lib/query-client.ts | React Query のセットアップ |
assets/lib/boss.ts | pg-boss ジョブキュー |
assets/lib/utils.ts | ユーティリティ関数 |
assets/components/providers.tsx | アプリのプロバイダー |
assets/components/theme-toggle.tsx | ライト/ダーク/システムの切り替え |
assets/components/team-switcher.tsx | チームのドロップダウン |
assets/config/globals.css | Linear スタイルのデザインシステム |
assets/config/Dockerfile | 本番用 Docker ビルド |
assets/config/railway.toml | Railway デプロイ |
assets/config/next.config.ts | Next.js の設定 |
assets/config/proxy.ts | セキュリティヘッダー |
assets/config/.env.example | 環境変数 |
assets/config/components.json | shadcn の設定 |
assets/prisma/schema.prisma | チーム用の基本スキーマ |
assets/api/teams-route.ts | チーム API テンプレート |
assets/api/invitations-route.ts | 招待 API テンプレート |
assets/hooks/use-teams.ts | React Query フック |
assets/types/next-auth.d.ts | 型拡張 |
セットアップ手順
1. プロジェクトを初期化する
bunx create-next-app@latest my-saas --typescript --tailwind --eslint --app --src-dir
cd my-saas
2. 依存関係をインストールする
# Core
bun add next-auth@beta @auth/prisma-adapter bcryptjs
bun add @prisma/client @prisma/adapter-pg pg
bun add -D prisma @types/bcryptjs
# UI
bun add lucide-react
bun add next-themes class-variance-authority clsx tailwind-merge
bun add @tanstack/react-query
# Validation
bun add zod
# Email
bun add resend
# Storage
bun add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
# Jobs
bun add pg-boss
# shadcn
bunx shadcn@latest init
bunx shadcn@latest add button card dialog input label sonner tooltip dropdown-menu avatar badge form command popover
3. 環境を設定する
.env.example をコピーし、値を入力します:
# Required
DATABASE_URL="postgresql://..."
AUTH_SECRET="openssl rand -base64 32"
NEXTAUTH_URL="http://localhost:3000"
RESEND_API_KEY="re_..."
EMAIL_FROM="[email protected]"
# S3 Storage (Railway)
AWS_ENDPOINT_URL="https://..."
AWS_ACCESS_KEY_ID="..."
AWS_SECRET_ACCESS_KEY="..."
AWS_S3_BUCKET_NAME="..."
AWS_DEFAULT_REGION="auto"
4. Prisma をセットアップする
# Initialize
bunx prisma init
# Copy the schema from assets/prisma/schema.prisma
# Ensure generator uses "prisma-client" (not "prisma-client-js")
# Ensure output is "../src/generated/prisma"
# Push to database
bunx prisma db push
bunx prisma generate
5. アセットファイルをコピーする
assets/ のテンプレートファイルをプロジェクトにコピーし、必要に応じてインポートを調整します。
デザインシステム
ブランドカラー
プライマリーパープル:#8b5cf6(HSL:262 83% 58%)
ライトモード
- 背景:
#ffffff - 前景:
hsl(240 10% 10%) - ボーダー:
hsl(240 6% 90%)
ダークモード(Linear スタイル)
- 背景:
#0A0A0B(hsl 240 5% 4%) - 前景:
hsl(0 0% 95%) - ボーダー:
rgba(255,255,255,0.06)
テーマの切り替え
アイコンのみの3択セグメントコントロール:
- 太陽アイコン → ライト
- 月アイコン → ダーク
- モニターアイコン → システム
サイドバーのフッターまたはヘッダーに配置します。attribute="class" を指定して next-themes を使用します。
タイポグラフィ
- フォント:Geist Sans / Geist Mono
- 文字間隔を狭める:本文は -0.011em、見出しは -0.02em
- フォントのスムージング:アンチエイリアス
API ルートのパターン
ルートハンドラーのシグネチャ(Next.js 15+)
重要: Next.js 15+ では、params は Promise であり、必ず await する必要があります。
type RouteParams = {
params: Promise<{ teamId: string }>;
};
export async function GET(req: NextRequest, { params }: RouteParams) {
const { teamId } = await params; // MUST await!
// ...
}
withErrorHandler の使用(推奨)
import { NextRequest, NextResponse } from "next/server";
import { requireTeamMember, parseJsonBody, withErrorHandler, ApiError } from "@/lib/api-helpers";
import { prisma } from "@/lib/prisma";
import { z } from "zod";
const createSchema = z.object({
name: z.string().min(1),
});
type RouteParams = {
params: Promise<{ teamId: string }>;
};
export const POST = withErrorHandler(async (req: NextRequest, { params }: RouteParams) => {
const { teamId } = await params;
// Auth check - throws 401/403
await requireTeamMember(teamId);
// Parse and validate body - throws 400
const { name } = await parseJsonBody(req, createSchema);
// Business logic
const result = await prisma.someModel.create({
data: { name, teamId },
});
return NextResponse.json(result, { status: 201 });
});
React Query のキー
// Factory pattern for query keys
export const teamKeys = {
all: ["teams"] as const,
lists: () => [...teamKeys.all, "list"] as const,
details: () => [...teamKeys.all, "detail"] as const,
detail: (id: string) => [...teamKeys.details(), id] as const,
members: (id: string) => [...teamKeys.detail(id), "members"] as const,
};
ミューテーションのパターン(シンプルな無効化)
export function useCreateTeam() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createTeam,
onSuccess: (newTeam) => {
queryClient.invalidateQueries({ queryKey: teamKeys.lists() });
queryClient.setQueryData(teamKeys.detail(newTeam.id), newTeam);
},
});
}
バックグラウンドジョブ
pg-boss のセットアップ
起動スクリプトを介して、Next.js と同じコンテナ内でジョブを実行します。
キューのパターン:
// Create job
await boss.send(QUEUES.SEND_EMAIL, payload, DEFAULT_JOB_OPTIONS);
// Worker handles job
boss.work(QUEUES.SEND_EMAIL, { batchSize: 1 }, async (job) => {
await handleSendEmail(job.data);
});
スケジュールされたジョブ
// Schedule recurring jobs on worker startup
await boss.schedule(QUEUES.CLEANUP, "0 6 * * *", {}); // Daily at 6 AM UTC
デプロイ
Dockerfile
Next.js サーバーと pg-boss ワーカーの両方を実行する単一コンテナ:
FROM node:22-alpine
RUN npm install -g bun
# ... build steps ...
CMD ["/app/start.sh"] # Runs both processes
Railway の設定
[build]
builder = "dockerfile"
[deploy]
healthcheckPath = "/"
healthcheckTimeout = 300
restartPolicyType = "on_failure"
next.config.ts のリライト
async rewrites() {
return [
{
source: "/uploads/:path*",
destination: "/api/uploads/:path*",
},
];
}
チェックリスト
- Next.js + TypeScript でプロジェクトを初期化
- 依存関係をインストール(上記の一覧を参照)
- 環境変数を設定
- User、Team、TeamMember、Invitation を含む Prisma スキーマ
- Credentials プロバイダーを使用した NextAuth v5
- サーバーサイドの認証チェックを備えたダッシュボードレイアウト
- API ヘルパー(withErrorHandler、requireTeamMember など)
- セキュリティヘッダー用の proxy.ts
- チームの作成と切り替え
- メンバー招待フロー
- ライト/ダークモードの切り替え
- React Query を設定
- Resend のメールサービス
- /uploads プロキシを備えた S3 ストレージ
- バックグラウンドジョブ用の pg-boss
- Railway デプロイ用の Dockerfile