メインコンテンツへスキップ

AWS 上でフルスタック app を ship するには、AWS Console を truth source にしない方がよい。Frontendsecretsdomainsstage rules は TypeScript で app の横に置き、pull request でレビューし、すべての environment で同じ方法で適用する。

それが SST が提供するものである。app 全体をコードで定義する——通常は単一の sst.config.ts から——SST が underlying AWS resources を自動化する。このノートは本サイトの infrastructure と DevOps モデルを扱う:Bun + Turborepo monorepo、OpenNext 上の Next.js、local、shared preview、production の stages。

3 つの infra ストーリーは混同しやすいので、ここでは分けて扱う:

  1. 今日 deploy されているものsst.config.ts は Next.js / OpenNext stack のみ import する。
  2. repo 内に定義済みだが未 import — container API infra(VpcClusterService + Router)は packages/infra にあり、その stack を deploy すべきになるまで run() からコメントアウトされたまま。
  3. 他の場所の関連パターン — Hono API 向け Lambda + API Gateway は Hono、Drizzle、Zod OpenAPI、SST による Backend API で扱う。本サイトの sst.config.ts が実行している内容ではない。

このノートはこれらの境界を取り巻く infra レイアウトと DevOps loop を担当する。



1. この Stack で SST を選ぶ理由

SST は、低レベルの resource を手で組み立てずに、Components として app の機能を定義できるよう設計されている。この stack では次の 3 点が最も重要——SST が Pulumi や CDK for Terraform などの汎用 IaC ツールと比較する際に強調する点と同じ(SST FAQ):

  1. ここで使う高レベル componentssst.aws.Nextjssst.Secret、(API module を接続するとき)sst.aws.Servicesst.aws.Routersst.aws.Vpcsst.aws.Cluster
  2. Resource linking — runtime code が hardcoded names や ARNs ではなく typed Resource SDK 経由で infrastructure を読める。
  3. sst dev を統一ローカル environment として——infra watcher、同一 CLI からの frontend(および後の container)dev processes、stage に Lambda handlers があるときの Live

本サイトでは、選んだ AWS region 上の OpenNext 経由 Next.js、stage ベースの domains(example.comdev.example.com{stage}.example.com)、non-prod の edge Basic Auth secrets、application packages と packages/infra を分けた monorepo に対応する。



2. SST と他の IaC の比較

これらのツールは同じ目標を持つ:infrastructure as code。primary audience と日常の friction が異なる。SST 自身の位置づけが有用である:SST は developers 向け、CDKTF と Pulumi は主に DevOps engineers 向け。SST は open-source Pulumi と bridged Terraform providers を内部で使う。Pulumi account は不要で、app state はチームの cloud account に残る(SST docs)。

Terraform と Vercel は他プロジェクトでも有用である。この フルスタック AWS app では SST がデフォルトである。これは workflow の選択であり、すべての org が Terraform を捨てるべきだという主張ではない。

ToolMental modelStrengthここで SST が app stacks に合う理由
Terraform / OpenTofuDeclarative HCL、provider 中心汎用、成熟した state と workflow;org 全体の platform teams に強いApp と 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実言語による汎用 IaC柔軟な providers;platform team が program を所有するときに良いSST は Pulumi 上に構築されつつ app components、linking、sst dev を追加;feature ごとに full Pulumi program は不要
Serverless Frameworkserverless apps 向け framework馴染みの Lambda packagingここで使う full-stack Next/OpenNext、containers、stage workflows より狭い
Vercel / PaaSGit push → hosted platformmanaged frontend だけなら高速 DX、AWS surface が小さいmanaged frontend のみなら強い;AWS resources、domains、secrets、optional ECS/API を TypeScript で所有したいときは SST

表の外:SST components は common app patterns をエンコードし、低レベル制御が必要なら transform150+ providers も使える。Linking は Terraform や CDK が手書き env vars と IAM で埋める runtime gap を閉じる。sst dev は自分で組む local stack ではなく product feature である。SST にも state がある——local と backup bucket——managed resources を Console で手編集すべきではない(Basics)。

platform team が Terraform や OpenTofu で shared networking を既に所有している、Pulumi や Terraform が multi-cloud control planes の team language である、CloudFormation-native CDK construct libraries が hiring と review の surface である場合、SST は poor fit になりうる。その場合でも SST は app delivery にはよく機能し、org レベル IaC はチームが既に leverage を持つ場所に残す。



3. プロジェクト形状:sst.config.tspackages/infra

SST は drop-in mode——Next.js app の横に単一 sst.config.ts——と monorepo——config を root に置き infrastructure を modules に分割——をサポートする。このプロジェクトは monorepo 形状を使う。

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 と safety rules を定義する。run function が infra modules を load する。今日 import されているのは Next.js のみ。API module はその stack を deploy すべきになるまでコメントアウト:


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")
  },
})

packages/infra は compose される pieces を保持する:

  • nextjs — OpenNext deployment と domains(今日 import)
  • secrets — edge Basic Auth credentials など stage secrets、API path 向け database と auth secrets
  • domain / edge — apex domain と non-prod edge Basic Auth
  • api / router / vpc / cluster — container API path、定義済みだが未 import

business logic はこれらのファイルから除外する。Infra modules は resources を create と link する。application packages が consume する。



4. この Stack の Components

OpenNext による Next.js

サイトは sst.aws.Nextjs で deploy する。production は apex domain。他 stage は {stage}.example.com。production は warm server instance を 1 つ。non-prod は warm: 0edge prop で non-prod Basic Auth。dev block は sst dev が local Next process をどう起動するかを指示する。


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",
  // buildCommand: sync assets, then OpenNext build
  edge,
  dev: {
    command: "bun run dev",
    directory: "apps/web",
    url: "http://localhost:3000",
  },
})

Secrets

Secrets は first-class resources であり、deployment model に入らない loose .env ではない。Edge Basic Auth は non-prod で username/password secrets。Database と auth secrets は API path 用に定義:


export const username = new sst.Secret("Username")
export const password = new sst.Secret("Password")
export const databaseUrl = new sst.Secret("DatabaseUrl")
export const betterAuthSecret = new sst.Secret("BetterAuthSecret")
export const aiGatewayApiKey = new sst.Secret("AiGatewayApiKey")

Container API(定義済み、未 import)

API module を import すると path は VpcClusterServiceRouterapi.example.com または {stage}.api.example.com。Database、Better Auth、AI Gateway secrets が service に link される。sst.config.tsawait import("./infra/api") を有効にするまで、その stack は deploy されない。

別形状——Lambda authorizer 付き API Gateway の背後の Lambda——は Hono、Drizzle、Zod OpenAPI、SST による Backend API のパターン。sst.aws.Functionsst.aws.ApiGatewayV2 を使う。Hono API 向けの related SST approach であり、本サイト config が現在 load している infrastructure ではない。



5. Hardcoded Env の代わりに Linking

Resource linking は infrastructure と runtime の橋である。resource を create し、link で渡し、SDK で読む。Linking は values を inject し、types(sst-env.d.ts)を生成し、component がサポートする範囲で permissions を付与する。

本サイトでは今日、Next.js stack が edge configuration 経由で edge Basic Auth credentials(username/password secrets)を pull する。web app 自体は database 用に Resource.* を読まない。

API module を import すると、secrets が container service に link され、application code は次のように読める:


import { Resource } from "sst"

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

frontend では linked values は server 側のみ。client components が見えるのは明示的に渡したものだけ。

stage secrets が必要な CLI——Drizzle migrations、Better Auth schema generation——には sst shell が linked stage environment を load し、2 つ目の secrets path を発明しない。



6. DevOps Unit としての Stages

SST では stage は environment:app の namespaced copy。本サイトの practical map は次のとおり:

StagePurposeDomain pattern
local / personal日常の sst devlocal Next URL;その stage の cloud resources
devShared previewdev.example.com
productionLive siteexample.com

production は protected。誤った sst remove で critical data を消さないよう、config は protect と production で removal: "retain" を使う。SST 推奨 template pattern と一致(removal policies)。

non-production stage には practical ops detail もある:CloudFront Basic Auth を edge viewerRequest injection で、preview URL をデフォルトで public にしない。

日常コマンドは短い:


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

credentials は local AWS credential chain(profile または SSO session)に残る。SST はその account と region に deploy する。Next.js と secret resources は Console をクリックして手作業では作らない。



7. ローカル Workflow:sst dev、Live、Mode Discipline

sst dev は local control plane である。本サイトの wired Next.js stack では日常価値がある部分は:

  1. infrastructure changes を deploy する watcher。
  2. Next.js dev block——local Next、stage の deployed resources に link。
  3. 後で API module を import すると、container dev command と optional tunnel で VPC resources へ。

Live——AWS の stub Lambda が local machine に proxy——は Lambda-heavy stages で最も重要。backend APIs ノートの Function / API Gateway pattern を含む。本サイトが OpenNext のみ import している間は less central だが、deployment shape によって OpenNext server paths も Lambda を含みうる。

sst devpersonal または local stage 向け。shared devproductionsst deploy。同一 stage を Live stub と real deploy で flip すると遅く混乱する:CLI が kill されると stub が残り、remote invoke が local machine を待って timeout する(Live quirks)。

shared URL は real stage を deploy する意味。Lambda handler を breakpoint で debug するなら personal stage で Live——Node handlers の debug には VS Code Auto Attach。



8. Production Safety と CI Boundary

IaC は state と process が honest なら役に立つ。

  • Console-edit しない SST が manage する resources。SST は config から state への diff を apply する。手編集は state を drift させる(Basics)。
  • production を protectprotectremoval: "retain" で accidental production remove 時に SST-managed stateful resources を消さない。API path の Postgres は sst.aws.Postgres ではなく sst.Secret 経由の external URL。
  • verify してから CI で deploy。 GitHub Actions は install、lint、format check、typecheck、tests、build を先に実行。checks 成功後のみ workflow が target stage(dev または production)で sst deploy。local deploy:dev / deploy scripts は intentional one-off deploy に有用だが、default path は CI:green checks、then deploy。

SST Console は git-push autodeploy、preview environments、monitoring の別経路。本サイトは verify-then-sst deploy sequence に GitHub Actions を使い、その loop に Console を必須にしない。

tenant isolation と SaaS boundary design は別問題——Hono、Better Auth、Drizzle、Postgres RLS による Multi-Tenant Backend を参照。



9. Mental Model

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 …

operating loop は小さく保つ:

  1. infra TypeScript または application code を変更。
  2. iterate 中は personal stage で sst dev
  3. pull request を開く。GitHub Actions が checks、tests、build を実行。
  4. 成功時、CI が target stage(dev または production)に sst deploy


Final Thoughts

SST は AWS infrastructure を app の横で review 可能な TypeScript に保つ。Components は common shipping features をエンコードする。Linking は env-var と IAM glue の class を除去する。Stages は environments を first-class DevOps unit にする。sst dev は local stack を 1 コマンドに畳む。

Terraform、CDK、Pulumi は org-wide platform ownership や team が既に standardize している問題には better fit。OpenNext 上 Next.js、stage-based domains、secrets、必要になれば container または Lambda API path には SST が strong default——developer loop を最適化し、resource graph だけではない。

Hono API on Lambda または Fargate——authorizer wiring、WAF、alarms 含む——は Hono、Drizzle、Zod OpenAPI、SST による Backend API を続ける。current component と CLI details は SST docs から。