Serverless Stackを試してみた

Serverless Stackを試してみた

Serverless Stack | Serverless Stack (SST)

Serverless Stack(SST)とは、サーバレスアプリを簡単に構築するためのフレームワークです。JavaScript/TypeScript,Python,Golangなどの言語に対応しています。

まだ、あまり有名ではない印象ですが、Githubのスター数や以下のニュースなどがあり、期待値が高いです。

Serverless Stack raises $1M for open-source application framework

今回は、このServerless Stackでどのようなことができるかを調査しました。

プロジェクトを作成

まずは動かしてみます。

以下のコマンドで、Go言語のプロジェクトを作成します。

$ npx create-serverless-stack@latest my-sst-app-go --language go
$ cd my-sst-app-go

作成されたプロジェクト内の stacks/Mytack.js でインフラの設定を行い、 src/ にアプリのプログラム(今回はGo言語プログラム)を書いていく流れとなります。

以下のコマンドで、開発環境をAWSにデプロイします。

$ npx sst start

コマンドを実行すると、デプロイ処理が走り、完了すると、API Gatewayのエンドポイントが表示されます。

Stack uchiko-test-my-sst-app-go-my-stack
  Status: deployed
  Outputs:
    ApiEndpoint: https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com

表示されたエンドポイントにブラウザでアクセスすると、メッセージが表示されます。

このメッセージは、生成されたGo言語プログラムの処理結果が表示されている形です。

src/main.go の中身を見ると、Go言語プログラムの内容が確認できます。

package main

import (
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
)

func Handler(request events.APIGatewayV2HTTPRequest)(events.APIGatewayProxyResponse, error) {
  return events.APIGatewayProxyResponse {
    Body: "Hello, World! Your was received at " + request.RequestContext.Time + ".",
    StatusCode: 200,
  }, nil
}

func main() {
  lambda.Start(Handler)
}

また、Live Lambda Devモードになっているので、Go言語プログラムを手元のPCで編集すると、即座に反映されます。

Live Lambda Devモードの仕組みについて気になる方は、以下のドキュメントを参照してください。

Live Lambda Development | Serverless Stack (SST)

APIを増やしてみる

APIを増やしてみます。

今回は、/echo?name=uchiko のようなリクエストを送ると Hello, uchiko というメッセージが返ってくるAPIを作成します。

まずGo言語プログラムです。

src/ 下に echo/ という名前のディレクトリを作成します。

そして、そのディレクトリに main.go というファイルを作成して、以下のようなプログラムを記述します。

package main

import (
    "fmt"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
)

func Handler(request events.APIGatewayV2HTTPRequest) (events.APIGatewayProxyResponse, error) {
    return events.APIGatewayProxyResponse{
        Body: fmt.Sprintf("Hello, %s", request.QueryStringParameters["name"]),
        StatusCode: 200,
    }, nil
}

func main() {
    lambda.Start(Handler)
}

リクエストから name に設定された値を抽出して、レスポンスに埋め込む処理を記述しています。

stacks/MyStack.js では、 /echo と上記のプログラムを関連付けます。

const api = new sst.Api(this, "Api", {
  routes: {
    "GET /": "src",
    "GET /echo": "src/echo", // 追記
  } 
})

以上で、完了です。ブラウザでアクセスしてみて、動作確認してみてください。

フロントエンドを作成してみる

Next.jsでフロントエンドを作成してみます。

以下のコマンドで、フロントエンド用のディレクトリを作成します。

$ npx create-next-app frontend

次に、SSTとNext間で環境変数を共有するために必要となるモジュールをインストールします。

$ cd frontend/
$ npm install @serverless-stack/static-site-env --save-dev

frontend/package.jsonscripts.dev を以下の内容に書き換えておきます。

"dev": "sst-env -- next dev"

frontend/pages/api/hello.js を以下の内容に書き換えて、APIリクエスト処理を実装します。

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

const apiEndpoint = process.env.API_ENDPOINT;

export default async function handler(req, res) {
  const name = req.query.name;
  const response = await fetch(`${apiEndpoint}/echo?name=${name}`);
  res.status(200).send(await response.text());
}

frontend/pages/index.js を以下の内容に書き換えて、UIを実装します。

import { useState } from 'react';

export default function App() {
  const [msg, setMsg] = useState(null);
  const [name, setName] = useState('');

  const onClick = async () => {
    const res = await fetch(`/api/hello?name=${name}`);
    setMsg(await res.text());
  }

  const onChangeName = (event) => {
    setName(event.target.value);
  };

  return (
    <div className="App">
      <input type="text" value={name} onChange={onChangeName}/>
      <button onClick={onClick}>Click Me!</button>
      {msg && <div>{msg}</div>}
    </div>
  )
}

stacks/MyStack.js で、フロントエンド用の設定を追記します。

const site = new sst.NextjsSite(this, "Site", {
  path: 'frontend',
  environment: {
    REGION: scope.region,
    API_ENDPOINT: api.url,
  },
});

site.attachPermissions([api]);

this.addOutputs({
  URL: site.url,
});

以下のコマンドで、バックエンド側をデプロイします。

$ npx sst start

また、フロントエンド側でも、開発用のサーバを起動します。

$ cd frontend/
$ npm run dev

以下のように、動作していることが確認できました。

感想

ここまでで、驚くほどかんたんにサーバレス開発ができることが分かりました。

現プロジェクトでは、Serverless Frameworkを使っており、設定ファイルを自動生成するようなスクリプトを一生懸命書いていました。それと比べるとSSTの開発体験はとても良い印象です。