🤖

SSTでVite+ReactアプリをAWSにデプロイする

2023/12/31に公開

前回はSSTでサーバーレスなAPIを開発・デプロイする方法を紹介しました.SSTってなに?って方はそちらを先に読んでください.

本記事ではSSTでReactアプリ(&簡単なAPI)をデプロイする流れを説明します.

今回のコードはGitHubにあります.
https://github.com/be4rr/sst-vite-app

SST&Viteセットアップ

まずSSTのプロジェクトを作成します.

npx create-sst@latest sst-vite-app
cd sst-vite-app
npm install

sst.config.tsを開き,このプロジェクトデプロイするリージョンを設定します.

sst.config.ts
  config(_input) {
    return {
      name: "sst-vite-app",
-     region: "us-east-1",
+     region: "ap-northeast-1",
    };
  },

続いてViteをセットアップします.packages/frontend/にViteプロジェクトを作成します.ここではReact+TypeScriptを選択しました.

cd packages
npm create vite@latest frontend
cd frontend
npm install

この時点でプロジェクトの構造は次のようになっています.

.
├── package-lock.json
├── package.json
├── packages
│   ├── core
│   ├── frontend  // ここにViteのプロジェクトが入っている
│   └── functions
├── pnpm-workspace.yaml
├── sst.config.ts
├── stacks
│   └── MyStack.ts  // AWSリソースを定義
└── tsconfig.json

Viteプロジェクトのpackage.json を次のように変更します.

packages/frontend/package.json
 "scripts": {
-    "dev": "vite",
+    "dev": "sst bind vite",
    "build": "tsc && vite build",

AWSリソースの定義

SSTプロジェクトにViteを含めるために,フロントエンドをSSTのスタック(≒AWSのリソース)として定義します.また単にフロントエンドをデプロイするだけでは面白くないので,フロントエンドから利用するためのAPIも定義します.

stacks/MyStack.tsを開き,次のコードで置き換えます.

stacks/MyStack.ts
import { Api, StackContext, StaticSite } from "sst/constructs";

export function MyApp({ stack }: StackContext) {
  const api = new Api(stack, "api", {
    routes: {
      "GET /": "packages/functions/src/lambda.handler",
    },
  });

  const web = new StaticSite(stack, "web", {
    path: "packages/frontend",
    buildOutput: "dist",
    buildCommand: "npm run build",
    environment: {
      VITE_API_ENDPOINT: api.url,
    }
  });

  stack.addOutputs({
    ApiEndpoint: api.url,
    WebUrl: web.url,
  });
}

sst.config.tsを次のように変更します.

sst.config.ts
import { SSTConfig } from "sst";
- import { API } from "./stacks/MyStack";
+ import { MyApp } from "./stacks/MyStack";

export default {
  config(_input) {
    return {
      name: "sst-vite-app",
      region: "ap-northeast-1",
      profile: "be4rr",
    };
  },
  stacks(app) {
-    app.stack(API);
+    app.stack(MyApp);
  },
} satisfies SSTConfig;

APIを利用するためにApp.tsxのコードを次のコードで置き換えます.ボタンをクリックするとAPIにリクエストを送信するだけのシンプルなプログラムです.

packages/frontend/src/App.tsx
import { useState } from "react";

function App() {
  const [data, setData] = useState<string>();

  return (
    <>
      <div>API Endpoint: {import.meta.env.VITE_API_ENDPOINT}</div>
      <button
        onClick={async () => {
          const res = await fetch(import.meta.env.VITE_API_ENDPOINT);
          const data = await res.text();
          setData(data);
        }}
      >
        Fetch
      </button>
      {data && <div>{data}</div>}
    </>
  );
}

export default App;

開発環境を実行

開発環境を実行します.

まずSSTプロジェクトのルートディレクトリsst-vite-app/npm run devを実行します.その後,別のターミナルでViteプロジェクトのルートディレクトリsst-vite-app/packages/frontend/npm run devを実行します.

するとSSTの開発環境がデプロイ・実行され,Viteの開発サーバーがローカルで起動します.

Reactのアプリを開いて表示されているボタンを押すと,APIにリクエストが送信されてそのレスポンスがUIに表示されます.

本番環境にデプロイ

このまま本番環境にデプロイします.先ほど起動したSSTとViteの開発環境は停止しておきます.

SSTプロジェクトのルートディレクトリsst-vite-app/で次のコマンドを実行します.

npx sst deploy --stage prod

このコマンドを実行することで,本番環境用にAPI GatewayやLambda関数が作成され,フロントエンドがビルドされてS3+CloudFrontにデプロイされます.

❯ npx sst deploy --stage prod
SST v2.38.7

➜  App:     sst-vite-app
   Stage:   prod
   Region:  ap-northeast-1
   Account: xxxxxxxxxxxx

Building static site packages/frontend

> frontend@0.0.0 build
> tsc && vite build

vite v5.0.10 building for production...
✓ 31 modules transformed.
dist/index.html                   0.46 kB │ gzip:  0.30 kB
dist/assets/index-T74ItOsL.css    0.92 kB │ gzip:  0.50 kB
dist/assets/index-WRPBg0DR.js   142.94 kB │ gzip: 45.95 kB
✓ built in 399ms
✔  Building...

|  MyApp PUBLISH_ASSETS_COMPLETE 
|  MyApp web/Distribution/Origin1/S3Origin AWS::CloudFront::CloudFrontOriginAccessIdentity CREATE_COMPLETE 
(略)
|  MyApp AWS::CloudFormation::Stack CREATE_COMPLETE 

✔  Deployed:
   MyApp
   ApiEndpoint: https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com
   WebUrl: https://xxxxxxxx.cloudfront.net

最後に出力されるWebUrlがデプロイされたCloudFrontのURLです.アクセスすると先ほど作成したReactアプリが表示され,APIからのデータ取得も同じ用にできるはずです.

カスタムドメインを設定する

フロントエンドにカスタムのドメインを設定します.以下ではRoute53にドメインを登録している前提で進めます.

私はbe4rr.clickというドメインを持っているので,フロントエンドに対してapp.be4rr.clickというサブドメインを設定したいと思います.

stacks/MyStack.ts
  const web = new StaticSite(stack, "web", {
    path: "packages/frontend",
    buildOutput: "dist",
    buildCommand: "npm run build",
    environment: {
      VITE_API_ENDPOINT: api.url,
    },
+    customDomain: {
+      domainName: "app.be4rr.click",
+      hostedZone: "be4rr.click",
+    },
  });

再び本番環境にデプロイします.証明書の発行やCloudFrontの設定で数分かかると思います.

npx sst deploy --stage prod

完了すると設定したドメイン名(私の場合はhttps://app.be4rr.click/)でフロントエンドにアクセスできるようになっているはずです.

後片付け

AWSにデプロイした開発環境と本番環境を削除します.

開発環境を削除

npx sst remove

本番環境を削除

npx sst remove --stage prod

Discussion