EN

使用 Next.js 16、Clerk 和 Supabase 构建全栈 AI SaaS 应用(2026)

逐步指南:构建一个包含身份验证、用量限制、订阅计费和 AI 功能的生产级 AI SaaS 应用

返回教程列表🌐 Read in English
高级55 分钟

使用 Next.js 16、Clerk 和 Supabase 构建全栈 AI SaaS 应用(2026)

逐步指南:构建一个包含身份验证、用量限制、订阅计费和 AI 功能的生产级 AI SaaS 应用

完整教程,教你使用 Next.js 16、Clerk 进行身份验证、Supabase 作为数据库、OpenAI 提供 AI 功能,构建全栈 AI SaaS 应用。涵盖用户管理、用量计量、Stripe 计费以及部署到生产环境。

使用 Next.js 16、Clerk 和 Supabase 构建全栈 AI SaaS 应用(2026)

本教程将从零开始构建一个完整的 AI 写作工具 SaaS。最终你将拥有一个可部署的应用,包含身份验证、用量限制、付费计划和 AI 功能。

架构概览


Next.js 16 (App Router)
├── 身份验证:Clerk (SSO、魔法链接、组织管理)
├── 数据库:Supabase (Postgres + pgvector)
├── AI:OpenAI API (GPT-4o)
├── 计费:Stripe (订阅)
└── 部署:Vercel

1. 项目设置

bash
npx create-next-app@latest ai-saas --typescript --tailwind --app
cd ai-saas
bun add @clerk/nextjs @supabase/supabase-js openai stripe @stripe/stripe-js ai @ai-sdk/openai

2. 使用 Clerk 进行身份验证

typescript
// src/middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher([ '/dashboard(.*)', '/api/ai(.*)', '/api/usage(.*)', ]);

export default clerkMiddleware((auth, req) => { if (isProtectedRoute(req)) auth().protect(); });

export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'], };

typescript
// src/app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs';

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); }

3. Supabase 数据库模式

sql
-- 用户表(从 Clerk 同步)
CREATE TABLE users (
  id TEXT PRIMARY KEY,  -- Clerk 用户 ID
  email TEXT NOT NULL,
  name TEXT,
  plan TEXT DEFAULT 'free' CHECK (plan IN ('free', 'starter', 'pro')),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 用量跟踪 CREATE TABLE usage_logs ( id BIGSERIAL PRIMARY KEY, user_id TEXT REFERENCES users(id), action TEXT NOT NULL, -- 'generate', 'improve', 'translate' tokens_used INTEGER DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW() );

-- 月度用量视图 CREATE VIEW monthly_usage AS SELECT user_id, DATE_TRUNC('month', created_at) AS month, COUNT(*) AS request_count, SUM(tokens_used) AS total_tokens FROM usage_logs GROUP BY user_id, DATE_TRUNC('month', created_at);

-- 计划限制 CREATE TABLE plan_limits ( plan TEXT PRIMARY KEY, monthly_requests INTEGER, monthly_tokens INTEGER );

INSERT INTO plan_limits VALUES ('free', 20, 50000), ('starter', 200, 500000), ('pro', 2000, 5000000);

4. 用量计量

typescript
// src/lib/usage.ts
import { createClient } from '@/lib/supabase/server';
import { auth } from '@clerk/nextjs/server';

export async function checkUsageLimit(action: string): Promise { const { userId } = await auth(); if (!userId) return false;

const supabase = createClient();

// 获取用户的计划和当前月度用量 const { data } = await supabase .from('users') .select(` plan, plan_limits!users_plan_fkey(monthly_requests) `) .eq('id', userId) .single();

const monthStart = new Date(); monthStart.setDate(1); monthStart.setHours(0, 0, 0, 0);

const { count } = await supabase .from('usage_logs') .select('*', { count: 'exact', head: true }) .eq('user_id', userId) .gte('created_at', monthStart.toISOString());

const limit = data?.plan_limits?.monthly_requests ?? 20; return (count ?? 0) < limit; }

export async function logUsage(action: string, tokensUsed: number) { const { userId } = await auth(); if (!userId) return;

const supabase = createClient(); await supabase.from('usage_logs').insert({ user_id: userId, action, tokens_used: tokensUsed, }); }

5. AI 功能

typescript
// src/app/api/ai/generate/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
import { auth } from '@clerk/nextjs/server';
import { checkUsageLimit, logUsage } from '@/lib/usage';

export async function POST(req: Request) { const { userId } = await auth(); if (!userId) { return new Response('Unauthorized', { status: 401 }); }

// 检查用量限制 const canProceed = await checkUsageLimit('generate'); if (!canProceed) { return new Response( JSON.stringify({ error: 'Monthly limit reached. Please upgrade your plan.' }), { status: 429, headers: { 'Content-Type': 'application/json' } } ); }

const { prompt, type } = await req.json();

const systemPrompts: Record = { blog: 'You are an expert blog writer. Create engaging, SEO-optimized content.', email: 'You are a professional email copywriter. Write clear, effective emails.', social: 'You are a social media expert. Create engaging posts optimized for each platform.', };

let tokensUsed = 0;

const result = streamText({ model: openai('gpt-4o'), system: systemPrompts[type] ?? systemPrompts.blog, prompt, onFinish: async ({ usage }) => { tokensUsed = usage.totalTokens; await logUsage('generate', tokensUsed); }, });

return result.toDataStreamResponse(); }

6. Stripe 计费

typescript
// src/app/api/stripe/create-checkout/route.ts
import Stripe from 'stripe';
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

const PLANS = { starter: process.env.STRIPE_STARTER_PRICE_ID!, pro: process.env.STRIPE_PRO_PRICE_ID!, };

export async function POST(req: Request) { const { userId, sessionClaims } = await auth(); if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

const { plan } = await req.json() as { plan: 'starter' | 'pro' };

const session = await stripe.checkout.sessions.create({ mode: 'subscription', payment_method_types: ['card'], line_items: [{ price: PLANS[plan], quantity: 1 }], success_url: ${process.env.NEXT_PUBLIC_APP_URL}/dashboard?upgraded=true, cancel_url: ${process.env.NEXT_PUBLIC_APP_URL}/pricing, customer_email: sessionClaims?.email as string, metadata: { userId, plan }, });

return NextResponse.json({ url: session.url }); }

typescript
// src/app/api/stripe/webhook/route.ts
import Stripe from 'stripe';
import { createAdminClient } from '@/lib/supabase/admin';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: Request) { const body = await req.text(); const sig = req.headers.get('stripe-signature')!;

let event: Stripe.Event; try { event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!); } catch { return new Response('Webhook signature verification failed', { status: 400 }); }

const supabase = createAdminClient();

if (event.type === 'checkout.session.completed') { const session = event.data.object as Stripe.CheckoutSession; const { userId, plan } = session.metadata as { userId: string; plan: string };

// 更新数据库中的用户计划 await supabase.from('users').upsert({ id: userId, plan }); }

if (event.type === 'customer.subscription.deleted') { // 取消订阅后降级为免费计划 const subscription = event.data.object as Stripe.Subscription; const userId = subscription.metadata.userId; await supabase.from('users').update({ plan: 'free' }).eq('id', userId); }

return new Response('OK', { status: 200 }); }

7. 定价页面

typescript
// src/app/pricing/page.tsx
import { auth } from '@clerk/nextjs/server';
import { PricingCards } from '@/components/pricing-cards';

export default async function PricingPage() { const { userId } = await auth();

const plans = [ { name: 'Free', price: '$0', period: 'forever', features: ['20 AI generations/month', 'Basic templates', 'Email support'], cta: userId ? 'Current Plan' : 'Get Started', disabled: true, }, { name: 'Starter', price: '$19', period: '/month', features: ['200 AI generations/month', 'All templates', 'Priority support', 'API access'], cta: 'Upgrade to Starter', plan: 'starter', highlighted: true, }, { name: 'Pro', price: '$49', period: '/month', features: ['2000 AI generations/month', 'Custom AI personas', 'Team collaboration', 'Analytics'], cta: 'Upgrade to Pro', plan: 'pro', }, ];

return (

Simple, transparent pricing

); }

8. 部署清单

bash

所需环境变量:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_... CLERK_SECRET_KEY=sk_... NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=... SUPABASE_SERVICE_ROLE_KEY=... OPENAI_API_KEY=sk-... STRIPE_SECRET_KEY=sk_... STRIPE_WEBHOOK_SECRET=whsec_... STRIPE_STARTER_PRICE_ID=price_... STRIPE_PRO_PRICE_ID=price_... NEXT_PUBLIC_APP_URL=https://your-app.vercel.app

结论

该架构处理了大多数 AI SaaS 需求:使用 Clerk 进行身份验证、Supabase 进行数据持久化、用量计量、Stripe 订阅计费以及 Vercel AI SDK 提供的 AI 功能。对于少于 100 个用户的后端总成本:约 $50/月(Supabase Pro + Vercel Pro + OpenAI 使用量)。盈利所需的收入:3 个 Starter 计划订阅者。

相关工具

所属主题:OpenAI 开发实战