Serverless 单兵作战:独立开发者的云端架构路线

前言
去年我朋友找我合作一个项目。他负责产品设计,我负责全栈开发。他问我:你的服务器在哪?我说:我没有服务器。他愣了一下------没有服务器怎么做产品?
我说的是真的。我的所有产品都跑在 Serverless 架构上,没有一台我需要自己维护的服务器。从用户注册、支付、AI 推理到定时任务,全部是"按需付费"的模式。
这篇文章就讲讲我作为"单兵作战"的独立开发者,怎么用 Serverless 架构撑起整套产品基础设施。
一、核心机制:单兵作战的云端架构设计
1.1 架构总览
graph TD
A["用户浏览器"] --> B["Vercel Edge Network"]
B --> C["Next.js App"]
C --> D["Vercel Functions"]
D --> E["Stripe 支付"]
D --> F["OpenAI API"]
D --> G["Supabase 数据库"]
D --> H["Vercel KV"]
C --> I["静态资源 CDN"]
style A fill:#6366f1,color:#fff
style D fill:#10b981,color:#fff
整个架构的核心思想是:每一个组件都是托管服务,我只管调用,不管维护。
1.2 各层选型决策
| 层级 | 选型 | 作为独立开发者的选择理由 |
|---|---|---|
| 前端托管 | Vercel | 免费套餐够用,自动 HTTPS,全球 CDN |
| 后端计算 | Vercel Functions | 零配置,按调用计费,自动扩缩容 |
| 数据库 | Supabase | 免费额度慷慨,支持实时订阅 |
| 缓存 | Vercel KV | 基于 Redis,API 简洁 |
| 文件存储 | Supabase Storage | 与数据库同一平台 |
| AI 能力 | OpenAI API | 无需 GPU,按 token 付费 |
| 支付 | Stripe | 开发者体验最好,文档优秀 |
| 域名 | Cloudflare DNS | 免费,配置简单 |
这个架构每月的基础开销是 0 元------直到产品开始有用户。
二、核心实现:搭建单兵云端架构
2.1 项目初始化
typescript
// 用 create-next-app 初始化项目
// npx create-next-app@latest my-project --typescript --tailwind --app
// 安装依赖
// npm install @supabase/supabase-js @vercel/kv stripe openai
2.2 数据库层:Supabase
typescript
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
export const supabase = createClient(supabaseUrl, supabaseKey);
用 Supabase 的 Row Level Security 做权限控制,不需要自己写中间件:
sql
-- 只允许用户查看自己的数据
CREATE POLICY "用户只能看自己的数据" ON 项目
FOR SELECT
USING (auth.uid() = 用户ID);
-- 只有付费用户才能创建超过 3 个项目
CREATE POLICY "付费用户可创建更多项目" ON 项目
FOR INSERT
WITH CHECK (
auth.uid() = 用户ID
AND (
(SELECT 订阅状态 FROM 用户 WHERE id = auth.uid()) = 'pro'
OR (SELECT COUNT(*) FROM 项目 WHERE 用户ID = auth.uid()) < 3
)
);
2.3 缓存层:Vercel KV
typescript
// lib/cache.ts
import { createClient } from '@vercel/kv';
const kv = createClient({
url: process.env.KV_REST_API_URL,
token: process.env.KV_REST_API_TOKEN,
});
export async function 缓存获取<T>(key: string, 获取函数: () => Promise<T>, ttl = 300): Promise<T> {
const 缓存 = await kv.get(key);
if (缓存) return JSON.parse(缓存);
const 数据 = await 获取函数();
await kv.set(key, JSON.stringify(数据), { ex: ttl });
return 数据;
}
// 使用示例
const 热门文章 = await 缓存获取('hot-articles', async () => {
const { data } = await supabase.from('文章').select('*').order('阅读量', { ascending: false }).limit(10);
return data;
}, 600);
2.4 API 层:Serverless Functions
typescript
// app/api/articles/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '@/lib/supabase';
import { 缓存获取 } from '@/lib/cache';
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const 页码 = parseInt(searchParams.get('page') || '1');
const 每页 = parseInt(searchParams.get('limit') || '20');
const 数据 = await 缓存获取(`articles:${页码}:${每页}`, async () => {
const { data, count } = await supabase
.from('文章')
.select('*', { count: 'exact' })
.range((页码 - 1) * 每页, 页码 * 每页 - 1)
.order('创建时间', { ascending: false });
return { 数据: data, 总数: count };
});
return NextResponse.json(数据);
}
export async function POST(req: NextRequest) {
const 文章 = await req.json();
const { data, error } = await supabase
.from('文章')
.insert({
标题: 文章.标题,
内容: 文章.内容,
用户ID: 文章.用户ID,
})
.select()
.single();
if (error) {
return NextResponse.json({ error: error.message }, { status: 400 });
}
return NextResponse.json(data, { status: 201 });
}
2.5 定时任务:Vercel Cron
typescript
// app/api/cron/daily-digest/route.ts
import { NextResponse } from 'next/server';
export const runtime = 'nodejs';
export async function GET() {
// 每天早上 8 点给用户发送日报
const 活跃用户 = await 获取昨日活跃用户();
for (const 用户 of 活跃用户) {
const 日报数据 = await 生成日报(用户.id);
await 发送邮件(用户.邮箱, '你的每日摘要', 日报数据);
}
return NextResponse.json({ 发送数: 活跃用户.length });
}
// vercel.json 配置
// {
// "crons": [
// {
// "path": "/api/cron/daily-digest",
// "schedule": "0 8 * * *"
// }
// ]
// }
三、单兵作战的经验
3.1 三个"不"原则
| 原则 | 说明 | 我的做法 |
|---|---|---|
| 不自己管服务器 | 任何需要 SSH 登录的场景都尽量避免 | 全部用托管服务 |
| 不自己搭基础组件 | 用户系统、支付、数据库都是现成的 | 用 Clerk / Supabase / Stripe |
| 不重复造轮子 | 有成熟方案就不自己写 | 先用开源,再按需定制 |
3.2 成本管理的技巧
Serverless 的按量付费对独立开发者友好,但也有陷阱:
- 数据库连接数:Supabase 免费版最多 10 个连接。如果函数并发太多可能不够用。解决方案是加一层缓存。
- 函数调用次数:Vercel 免费版每月 100k 次调用。个人项目通常用不完。
- KV 存储:按存储量计费,缓存记得设过期时间。
四、总结
独立开发者的核心优势是灵活性------别人需要三周才能完成的流程,我三天就能跑通,靠的就是 Serverless 架构的零摩擦。
你可以把这套架构看作一条"云端流水线":写代码、部署、上线、迭代。每一环都有对应的托管服务帮你在跑,你要做的就是把它们串起来。
一个人就是一支队伍------关键在于不要让基础设施的复杂度拖慢你的迭代速度。