首页 / 文章

从发布Next.js SaaS产品及构建脚手架类命令行工具中获得的经验教训。

阐述了各项核心决策——框架选择、身份验证、多租户架构、计费方式以及状态管理——同时还介绍了如何构建能够简化Next.js项目设置的命令行工具。

阅读约 6 分钟1331 词

使用 React 和 Next.js 开发真正的 SaaS 产品,关键不在于选择时髦的库,而在于在接触任何界面之前就正确地做出数十个看似平凡的小决策。让产品架构能够承受真实用户需求的那种严谨性,同样体现在创建新的 Next.js 项目这样的基础工作中——正因如此,用于生成模板代码的自制 CLI 工具最终也能传达与构建 SaaS 产品本身相同的道理:正是那些枯燥的设置工作决定了项目的成败。

选择不会拖慢你速度的技术栈

在开发了多个 SaaS 产品之后,技术栈往往会形成一种可预测的模式:

  1. Next.js 作为处理路由、服务器端渲染和 API 路由的统一框架,这样就不需要为不同功能分别使用不同的工具。
  • TypeScript是必不可少的,尤其是在处理账单逻辑的代码中——这类错误绝不能在运行时出现。
  • 使用Tailwind CSS进行样式设计,主要是因为它能避免不断与CSS特异性问题作斗争。
  • Redux Toolkit或Zustand则专门用于那些确实需要全局共享的状态管理。
  • 选择这些技术并非出于追求新奇。它们能让小团队高效工作,同时避免悄悄积累日后无力偿还的技术债务。

    先打好基础

    创始人很容易直接着手构建仪表板,但应抵制这种冲动。在开发任何面向用户的界面之前,必须先完成以下三件事:

    1. 身份验证——采用真实会话实现,而非仅依赖存储在localStorage中的令牌。
    2. 多租户支持——即便目前只有单一客户,也应从一开始就为多个客户设计系统。
    3. 计费回调机制——尽早配置Stripe webhook,可避免产品正式上线后不得不进行繁琐的代码重写。

    以下是一种值得采用的模式,可避免在调用租户级API时重复编写代码:

    // hooks/useTenantFetch.ts
    import { useCallback } from "react";
    import { useSession } from "next-auth/react";
    
    export function useTenantFetch() {
      const { data: session } = useSession();
    
      const fetchScoped = useCallback(
        async (endpoint: string, options: RequestInit = {}) => {
          if (!session?.tenantId) throw new Error("No active tenant");
    
          const res = await fetch(`/api/${session.tenantId}${endpoint}`, {
            ...options,
            headers: {
              ...options.headers,
              "Content-Type": "application/json",
            },
          });
    
          if (!res.ok) throw new Error(`Request failed: ${res.status}`);
          return res.json();
        },
        [session]
      );
    
      return { fetchScoped };
    }
    

    这虽只是一个小工具,但能让你日后不必在数十个组件中分散处理租户ID的验证工作。

    明确哪些内容应放在全局状态中

    并非所有数据都适合存放在 Redux 中。一家由 Y Combinator 支持的初创公司的一名工程师说得很到位:全局状态就像会随时间产生利息的债务。应严格将 Redux(或你选择的其他全局存储)用于那些真正需要在整个界面中使用的功能——如模态框、特性开关、用户偏好设置——而将那些实际上只是伪装成数据的服务器数据交给 React Query 或 SWR 来处理。

    以下几点总结了构建 SaaS 应用时的关键要点:

    • 在投入精力打造精美界面之前,先确保身份验证和计费功能正常运行。
    • 一旦涉及资金问题,TypeScript 就不再是可选的。
    • 避免过度集中管理状态;大多数功能应由服务器端状态管理工具来处理,而非全局存储。

    这一切并非源于追逐最新的技术库,而是源自基本原则:尽早确定认证、租户管理和计费机制,如此之后的一切——业务扩展、新功能添加、系统规模提升——都会变得容易得多。

    当搭建过程本身成为瓶颈

    正是那种想要消除重复且易出错的搭建工作的意识,最终促使人们将这些常规步骤自动化。每位前端开发者都熟悉这样的流程:先用 create-next-app 创建新项目,随后繁琐的工作才开始。你需要创建 components 文件夹,将 ui 与 features 分开,设置 hooks,连接共享的 types,编写工具函数,配置路径别名,并整理文件夹结构——往往一个小时过去后还写不出哪怕一行真正的业务逻辑代码。

    在重复执行该流程太多次后,自然而然的下一步就是将其自动化,于是第一个开源的 npm 包诞生了:nexarch-cli。

    根本问题

    标准的 Next.js 启动模板只提供了一块空白画布,而非可直接投入生产的架构。现代应用通常需要:

    • 清晰的展示逻辑与业务逻辑分离
    • 基于功能的模块化分组
    • 可扩展的类型定义与工具封装
    • 整个团队都能遵循的一致规范

    为每个原型或客户项目手动创建这些内容既缓慢又缺乏一致性,还容易出错。

    nexarch-cli的实际功能

    nexarch-cli 是一款交互式命令行工具,能够几秒钟内搭建出整洁且模块化的 Next.js 架构。无需手动创建嵌套文件夹和占位文件,只需在终端中回答几道提示问题,该工具便会为你生成基础结构。

    它的主要功能包括:

    • 通过针对需求设计的简单终端提示实现交互式框架搭建。
    • 具备预配置的规范,可支持应用发展时的模块化架构扩展。
    • 无需全局安装——可通过 npx 按需运行。
    • 遵循当前 Next.js 和 React 的规范,使目录结构与现代开发实践保持一致。

    运行方式

    无需全局安装任何内容,可以直接运行它:

    npx nexarch-cli
    

    回答完提示后,项目结构就会生成,便可开始实际开发。

    构建首个命令行工具的经验教训

    将此工具作为第一个开发者工具进行构建和发布的过程充满了学习挑战。其中一些重要的经验包括:

    • 终端用户体验与底层代码同样重要。正确处理进程退出、呈现易读的提示信息以及给出清晰的错误消息,这些都与生成文件的逻辑同样重要。
    • 文件系统操作存在许多边缘情况。要编写能在不同操作系统上保持一致行为的递归目录创建工具和文件模板,需要经过仔细且反复的测试。
  • NPM 的打包方式有其独特的学习曲线。正确配置 package.json 中的二进制文件目录(bin)、使用 chmod 处理文件权限,以及将依赖项带来的开销降至接近零——这些内容都揭示了基于 Node 的 CLI 工具在底层的工作原理。
  • 项目目前的进展

    该工具仍处于早期阶段,属于开源项目,在不断完善过程中能够从外部反馈中受益。其代码存储在 github.com/CaffeineLogs/nexarch-cli,而已发布的包则可在 npm 注册表中以 nexarch-cli 为名称找到。

    任何有不同偏好的项目结构,或遇到脚手架尚未妥善处理的场景的人,都欢迎在仓库中将其作为问题提出。关于未来可能添加的功能的建议——比如打包好的样式设置或现成的状态管理预设——也同样欢迎作为讨论点或评论提出。

    无论你是决定哪些功能应纳入 Redux,还是规划一个新的 Next.js 项目的结构,都有一个规律:项目中最繁琐的部分——身份认证、租户管理、计费、文件夹结构——恰恰是那些最值得妥善处理、甚至应该优先自动化的部分。