跳至主要內容

在 AWS 上交付全端應用時,最好不要把 AWS Console 當成真相來源。Frontend、secrets、domains 與 stage rules 都應該用 TypeScript 寫在應用旁邊,經 pull request 審視,並在每個環境以同樣方式套用。

SST 用程式碼定義應用——通常從單一 sst.config.ts 開始——並自動化底層 AWS resources。本站是 Bun + Turborepo monorepo,Next.js 跑在 OpenNext 上,並有 local、shared preview 與 production 的 stages。

三種 infra 故事分開處理:

  1. 目前已部署 — sst.config.ts 只 import Next.js / OpenNext stack。
  2. 已在 repo 裡定義,但尚未 import — container API infra(Vpc → Cluster → Service + Router)放在 packages/infra,並在 run() 裡保持註解,直到該 stack 需要部署。
  3. 其他地方的相關模式 — 用 Lambda + API Gateway 做 Hono API 見 用 Hono、Drizzle、Zod OpenAPI 與 SST 打造 Backend APIs。那篇筆記並不是本站 sst.config.ts 正在跑的內容。


1. 為什麼這套 stack 選 SST

SST 讓開發者可以用 Components 定義應用功能,而不必親手組裝每一個低階 resource。

  • 較高階的 components,這裡用到:sst.aws.Nextjs、sst.Secret,以及(當 API module 接上時)sst.aws.Service、sst.aws.Router、sst.aws.Vpc 與 sst.aws.Cluster。
  • Resource linking,讓 runtime 程式碼透過 typed Resource SDK 讀取基礎設施,而不是 hardcoded names 與 ARNs。
  • sst dev 作為統一的本地環境:infra watcher、來自同一 CLI 的 frontend(以及之後的 container)dev processes,再加上當 stage 裡有 Lambda handlers 時的 Live。

本站:經 OpenNext 的 Next.js、以 stage 為基礎的 domains、非 prod 的 edge Basic Auth,以及與 packages/infra 分開的應用 packages。SST 與 Pulumi 或 CDK for Terraform 的比較見 SST FAQ。



2. SST 與其他 IaC 的比較

這些工具都共享 infrastructure as code。SST 面向 developers;CDKTF 與 Pulumi 主要面向 DevOps engineers。SST 底層仍使用 open-source Pulumi 與 bridged Terraform providers;它不需要 Pulumi account,應用 state 也留在團隊自己的 cloud account(SST docs)。

ToolMental modelStrength為什麼 SST 適合這裡的 app stacks
Terraform / OpenTofuDeclarative HCL,以 provider 為中心通用、成熟的 state 與 workflow;適合組織級 platform teamsApp 與 infra 仍然分開;對這個 monorepo 沒有 first-class Live、linking,或 Nextjs DX
AWS CDKTypeScript constructs → CloudFormation深度 AWS 覆蓋與 Constructs 生態AWS-native modeling 很強;對這個 workflow 的統一 app loop(linking、sst dev、frontend + backend multiplexer)較弱
Pulumi / CDKTF用真實語言寫的通用 IaCProviders 靈活;適合 platform team 擁有整支 program 的情況SST 建在 Pulumi 之上,但加上 app components、linking 與 sst dev,不必為每個 feature 寫完整 Pulumi program
Serverless Framework聚焦 serverless apps 的 framework熟悉的 Lambda packaging比這裡用到的全端 Next/OpenNext、containers 與 stage workflows 更窄
Vercel / PaaSGit push → hosted platform快速 frontend DX,幾乎不用碰 AWS 表面只需要 managed frontend 時很強;當需要以 TypeScript 擁有 AWS resources、domains、secrets 與可選的 ECS/API 時選 SST

  • SST components 編碼常見 app patterns,仍允許在需要更低階控制時使用 transform 加上 150+ providers。
  • Linking 補上 Terraform 或 CDK 通常靠手寫 env vars 與 IAM 填補的 runtime 缺口。sst dev 是產品功能,而不是自己組裝的本地 stack。
  • SST 仍然有 state——本地加上 backup bucket——所以 managed resources 不應在 Console 人手編輯(Basics)。那份 state 的 backup,以及何時 snapshot 不是 failover,見 System Design 裡的 Disaster Recovery。

當 platform team 已經用 Terraform 或 OpenTofu 擁有 shared networking、當 Pulumi 或 Terraform 是團隊多雲 control planes 的語言,或當 CloudFormation-native CDK construct libraries 是招聘與 review 表面時,SST 並不適合。



3. 專案形狀:sst.config.ts 與 packages/infra

SST 支援 drop-in mode——在 Next.js app 旁邊放單一 sst.config.ts——以及 monorepo,config 留在 root,基礎設施拆成 modules。這個專案用 monorepo 形狀。

text
sst.config.ts          app name, region, protect / removal, imports
packages/infra/        Next.js, secrets, domain, edge, optional API/VPC
apps/web               Next.js site
apps/api               Hono / Mastra API
packages/*             shared auth, db, content, design system

Config 檔案定義 app identity 與安全規則。run function 載入 infra modules。今天只 import Next.js;API module 保持註解,直到該 stack 需要部署:


sst.config.ts
export default $config({
  app(input) {
    return {
      name: "my-app",
      removal: input?.stage === "production" ? "retain" : "remove",
      protect: ["production"].includes(input?.stage),
      home: "aws",
      providers: {
        aws: {
          region: "us-east-1",
        },
      },
    }
  },
  async run() {
    // await import("./infra/api")
    await import("./infra/nextjs")
  },
})

  • nextjs — OpenNext 部署與 domains(今天已 import)
  • secrets — stage secrets,例如 edge Basic Auth credentials,以及 API 路徑的 database 與 auth secrets
  • domain / edge — apex domain 與非 prod 的 edge Basic Auth
  • api / router / vpc / cluster — container API 路徑,已定義但尚未 import

業務邏輯不放在這些檔案裡。Infra modules 建立並 link resources;應用 packages 消費它們。



4. 這套 Stack 裡的 Components

本站用 sst.aws.Nextjs 部署。Production 用 apex domain;其他 stages 用 {stage}.example.com。


packages/infra/nextjs.ts
const isProd = $app.stage === "production"
const domain = "example.com"

export const nextjs = new sst.aws.Nextjs("Web", {
  path: "apps/web",
  domain: {
    name: isProd ? domain : `${$app.stage}.${domain}`,
    redirects: isProd ? [`www.${domain}`] : [],
  },
  warm: isProd ? 1 : 0,
  environment: {
    NEXT_PUBLIC_SITE_URL: `https://${domain}`,
    SST_STAGE: $app.stage,
  },
  openNextVersion: "4.0.3",
  edge,
  dev: {
    command: "bun run dev",
    directory: "apps/web",
    url: "http://localhost:3000",
  },
})

  • Production 保留一個 warm server instance;非 prod 用 warm: 0。edge prop 接上非 prod Basic Auth。dev block 告訴 sst dev 如何啟動本地 Next process。
  • Secrets 是 first-class resources,不是鬆散的 .env 檔案:sst.Secret("Username")、Password、DatabaseUrl、BetterAuthSecret、AiGatewayApiKey。
  • 當 API module 被 import 時,路徑是 Vpc → Cluster → Service,再用 Router 做 api.example.com 或 {stage}.api.example.com。在啟用 await import("./infra/api") 之前,那個 stack 不會部署。
  • 另一種形狀——Lambda 在 API Gateway 後面,加上 Lambda authorizer——是 用 Hono、Drizzle、Zod OpenAPI 與 SST 打造 Backend APIs 裡的模式。那用 sst.aws.Function 與 sst.aws.ApiGatewayV2。它不是本站 config 目前載入的基礎設施。


5. 用 Linking 取代 Hardcoded Env

Resource linking 是基礎設施與 runtime 之間的橋。建立一個 resource,放進 link,再用 SDK 讀取。

  • Linking 注入 values、產生 types(sst-env.d.ts),並在 component 支援的地方授予 permissions。
  • 本站今天,Next.js stack 透過 edge configuration 拉取 edge Basic Auth credentials。Web app 本身並不為 database 讀取 Resource.*。
  • 當 API module 被 import 時,secrets 會被 link 進 container service。

ts
import { Resource } from "sst"

const client = postgres(Resource.DatabaseUrl.value, {
  ssl: "require",
})

  • Linked values 只在 server 側可用。Client components 只能看見明確傳進去的東西。
  • 對需要 stage secrets 的 CLIs——Drizzle migrations、Better Auth schema generation——sst shell 載入 linked stage environment,不必發明第二條 secrets 路徑。


6. Stages 作為 DevOps 單位

在 SST 裡,stage 是一個環境:應用的一份 namespaced copy。

StagePurposeDomain pattern
local / personal日常 sst dev本地 Next URL;該 stage 的 cloud resources
devShared previewdev.example.com
productionLive siteexample.com

  • Production 受保護。意外的 sst remove 不應抹掉關鍵資料,所以 config 在 production 上用 protect 與 removal: "retain"(removal policies)。
  • 非 production stages 透過 edge viewerRequest injection 拿到 CloudFront Basic Auth,所以 preview URLs 預設不公開。
  • Credentials 留在本地 AWS credential chain(profile 或 SSO session)。SST 部署進那個 account 與 region。Next.js 與 secret resources 不是靠在 Console 點出來的。

bash
aws sso login               # refresh AWS credentials
sst dev --stage local       # personal / local stage
sst deploy --stage dev      # shared preview
sst deploy --stage production


7. 本地 Workflow:sst dev、Live 與 Mode Discipline

sst dev 是本地 control plane。

  • 一個會部署基礎設施變更的 watcher。
  • Next.js dev block——本地 Next,link 到該 stage 已部署的 resources。
  • 之後,當 API module 被 import 時,container dev command,以及可選的 tunnel 到 VPC resources。
  • Live——AWS 裡把請求 proxy 到本機的 stub Lambdas——對 Lambda-heavy stages 最重要,包括 backend APIs 筆記裡的 Function / API Gateway 模式。在本站只 import OpenNext 時,它沒那麼核心。

sst dev 屬於 personal 或 local stage。Shared dev 與 production 用 sst deploy。Shared URL 意味著部署一個真正的 stage。用 breakpoints 除錯 Lambda handler 意味著在 personal stage 上用 Live——除錯 Node handlers 時搭配 VS Code Auto Attach。

Failure: 在同一個 stage 上在 Live stubs 與真正 deploys 之間翻轉。若 CLI 被殺掉,stubs 可能留下,remote invokes 會逾時等待本機(Live quirks)。



8. Production 安全與 CI 邊界

IaC 只有在 state 與流程保持誠實時才有用。

  • 不要在 Console 編輯 SST 管理的 resources。SST 會依 config 對 state 套用 diffs;人手編輯會讓 state drift(Basics)。
  • 保護 production:用 protect 與 removal: "retain"。API 路徑用的 Postgres 是經 sst.Secret 的外部 URL,不是這個 config 建立的 sst.aws.Postgres。
  • 先驗證,再在 CI 部署。 GitHub Actions 先跑 install、lint、format check、typecheck、tests 與 build。只有這些 checks 成功後,workflow 才對 dev 或 production 跑 sst deploy。
  • 本地 deploy:dev / deploy scripts 仍適合刻意的一次性 deploys。預設路徑是 CI:checks 全綠,再 deploy。
  • SST Console 是另一種取得 git-push autodeploy 的方式。本站用 GitHub Actions 做 verify-then-sst deploy 序列。

Tenant isolation 與 SaaS 邊界設計是另一個問題——見 用 Hono、Better Auth、Drizzle 與 Postgres RLS 打造 Multi-Tenant 後端。



9. Mental Model

text
sst.config.ts
  → packages/infra/nextjs   (imported today)
      → Nextjs / OpenNext + edge Basic Auth secrets
  → packages/infra/api      (defined, not imported)
      → Vpc / Cluster / Service / Router + linked secrets
  → link (when a consumer is linked)
      → Resource.* in app runtime
      → sst shell for CLIs

stages: local | dev | production
local: sst dev
ci: checks → tests → build → sst deploy --stage …

SST 讓 AWS infrastructure 成為可 review 的 TypeScript,並放在應用旁邊。當問題是組織級 platform ownership 時,Terraform、CDK 或 Pulumi 仍然更合適。目前的 component 與 CLI 細節,從 SST docs 開始。關於 Lambda 或 Fargate 上的 Hono API,繼續看 用 Hono、Drizzle、Zod OpenAPI 與 SST 打造 Backend APIs。


Recap Q&A

閱讀下一篇筆記
SOLID 作為變更隔離