mcpField
T
team-saas

team-saas

github.com/blink-new開発57 回インストール
リポジトリ

team-saas について

team-saasは、チームやワークスペース、メンバー招待を備えたマルチテナントSaaSの構築向けアーキテクチャスキルです。認証、バックグラウンドジョブ、デプロイ設定を含むNext.jsの設計パターンとテンプレートを提供します。

主な機能

  1. 01マルチテナントのチームとメンバー権限
  2. 02認証とチーム招待フロー
  3. 03APIルートの権限パターン
  4. 04バックグラウンドジョブキュー設定
  5. 05UIテーマとコンポーネントテンプレート
  6. 06DockerとRailwayのデプロイ設定

活用シーン

  1. 01チームワークスペース付きSaaSの立ち上げ
  2. 02チーム招待と認可の実装
  3. 03Next.jsアプリへのバックグラウンドジョブ追加
  4. 04Railwayデプロイ設定の準備

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/blink-new/claude --skill team-saas

インストール後、Claude Code で/team-saasを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/api/
accept-invitation-route.ts
invitations-route.ts
teams-route.ts
uploads-proxy-route.ts
assets/components/
auth-layout.tsx
dashboard-layout.tsx
providers.tsx
root-layout.tsx
team-switcher.tsx
theme-toggle.tsx
assets/config/
components.json
Dockerfile
globals.css
next.config.ts
prisma.config.ts
proxy.ts
railway.toml
tsconfig-paths.json
assets/hooks/
use-teams.ts
assets/lib/
api-helpers.ts
auth.config.ts
auth.ts
boss.ts
prisma.ts
query-client.ts
resend.ts
s3.ts
utils.ts
assets/prisma/
schema.prisma
assets/types/
next-auth.d.ts

詳細

チーム向け SaaS アーキテクチャスキル

チームワークスペース、メンバー招待、認証、モダンな UI を備えた、本番環境に対応するマルチテナント SaaS アプリケーションを構築します。実際の本番アプリケーションを支える、実績あるアーキテクチャに基づいています。

このスキルを使用する場面

次の場合に使用します:

  • チーム/ワークスペース機能を備えた新しい SaaS プロダクトを構築する
  • マルチテナントの認証と認可を設定する
  • チームメンバーの招待フローを実装する
  • ライト/ダークモードを備えた Linear/Vercel スタイルのモダンな UI を作成する
  • SaaS 向けのバックグラウンドジョブ処理を設定する
  • フルスタックの Next.js アプリを Railway にデプロイする

技術スタックの概要

レイヤーテクノロジーバージョン
フレームワークNext.js (App Router)16.x
ReactReact19.x
ランタイムBunLatest
データベースPostgreSQL (Railway)-
ORMPrisma7.x
認証NextAuth v5 (Auth.js)5.0.0-beta
UIshadcn/ui (new-york)Latest
スタイリングTailwind CSSv4
テーマ設定next-themesLatest
アイコンLucide ReactLatest
状態管理TanStack React Query5.x
バリデーションZod4.x
メールResend6.x
ストレージRailway S3-compatible-
ジョブpg-boss12.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.tsCredentials プロバイダーを使用する NextAuth v5
assets/lib/auth.config.ts認証設定(コールバック、カスタムページ)
assets/lib/api-helpers.tswithErrorHandler、requireTeamMember、ApiError
assets/lib/prisma.tspg アダプターを使用する遅延ロードの Prisma クライアント
assets/lib/s3.ts事前署名 URL を扱う S3 ユーティリティ
assets/lib/resend.tsResend メールクライアントとテンプレート
assets/lib/query-client.tsReact Query のセットアップ
assets/lib/boss.tspg-boss ジョブキュー
assets/lib/utils.tsユーティリティ関数
assets/components/providers.tsxアプリのプロバイダー
assets/components/theme-toggle.tsxライト/ダーク/システムの切り替え
assets/components/team-switcher.tsxチームのドロップダウン
assets/config/globals.cssLinear スタイルのデザインシステム
assets/config/Dockerfile本番用 Docker ビルド
assets/config/railway.tomlRailway デプロイ
assets/config/next.config.tsNext.js の設定
assets/config/proxy.tsセキュリティヘッダー
assets/config/.env.example環境変数
assets/config/components.jsonshadcn の設定
assets/prisma/schema.prismaチーム用の基本スキーマ
assets/api/teams-route.tsチーム API テンプレート
assets/api/invitations-route.ts招待 API テンプレート
assets/hooks/use-teams.tsReact 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

コメント