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

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 项目初始化

// 用 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

// 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 做权限控制,不需要自己写中间件:

-- 只允许用户查看自己的数据
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

// 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

// 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

// 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 架构的零摩擦。

你可以把这套架构看作一条"云端流水线":写代码、部署、上线、迭代。每一环都有对应的托管服务帮你在跑,你要做的就是把它们串起来。

一个人就是一支队伍——关键在于不要让基础设施的复杂度拖慢你的迭代速度

更多推荐