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

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

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

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

相关推荐
smallYoung1 分钟前
Vibe Coding 笔记-中
人工智能
米小虾17 分钟前
DSpark:让大模型"写得更快"的秘密武器
人工智能·deepseek
JavaGuide29 分钟前
比 iTerm2 更适合 Claude Code/Codex 的终端,我换成 Ghostty 了
人工智能·后端
threerocks40 分钟前
神级 Skill,作品个个儿爆,我开源了长期自用的手绘风格库
人工智能·aigc
小爷毛毛_卓寿杰2 小时前
我把一个 3B 模型塞进了 Xinference,然后它干掉了 DeepSeek V3.2
人工智能·开源·github
秦先生在广东2 小时前
Agent 闭环才是真正的护城河:Anthropic “300 个 Agent“ 背后被忽视的秘密
人工智能
Bigfish_coding2 小时前
前端转agent-【python】- 14 记忆系统优化:摘要与遗忘
人工智能
Bigfish_coding2 小时前
前端转agent-【python】-13 Ollama Python流式输出教程:stream=True 与 async 实践
人工智能
字节跳动数据库4 小时前
文章分享——相似函数处理方法
人工智能·后端·程序员
Bigfish_coding4 小时前
前端转agent-【python】-12 LangChain 入门实战:RAG + LCEL 链式调用
人工智能