🍙

Serverless Frameworkで作成したプロジェクトでexpressを使う with TypeScript

2021/07/25に公開

こんにちは!

最近個人で開発しているアプリで、ServerlessFrameworkで作成したプロジェクトでexpressを使うことがありました。その中で色々調べていたのですが、ymlを使って設定を書いているものが多く、tsファイルに設定を書いているものはあまり見つかりませんでした。

ですので本記事では、tsファイルで設定を書き、serverlessでexpressを使う方法を紹介していきます。

目次

  • アプリの構成。この改修が必要になった理由
  • serverlessアプリ作成
  • serverless offlineインストール
  • express, serverless-httpインストール
  • URLを変える

アプリの構成。この改修が必要になった理由

まず何でServerlessFrameworkを使うことになったのかご紹介します。
個人開発でアプリを作成しているのですが、その構成が以下のような状態でした。

DBがあるEC2がパブリックサブネットにあるのはセキュリティ的に良くないなということで、プライベートサブネットに移したいと思っていました。

しかしserver側ソースを別のEC2にあげ、1台はパブリックサブネット、もう1台はプライベートサブネットに置くとなるとawsの料金が高くなるのでしたくありませんでした。

そこでserver側ソースとDBが入ったEC2をプライベートサブネットに持っていき、ServerlessFrameworkで構築したアプリをパブリックサブネットに置き、プロキシ的な役割を果たしてもらおうと考えたのが今回の改修理由です。

目指すのは以下のような構成ですね。

serverlessアプリ作成

以下のコマンドを実行しTypeScriptのserverlessアプリを作成します。

npx serverless create --template aws-nodejs-typescript --name アプリ名 --path アプリ名

serverless offlineをインストール

ソースを変更する度にaws環境にデプロイして確認するのは面倒なので、ローカル環境で実行できるようserverless-offlineをインストールします。

npm install -D serverless-offline

インストールが完了してから、serverless.tsのpluginsにserverless-offlineを追加します。
おそらく最初からserverless-webpackがpluginsの配列に入っているので、serverless-offlineを追加すると以下のようになります。

plugins: ['serverless-webpack', 'serverless-offline'],

以下のコマンドでローカルで起動できます。

npx sls offline --httpPort 4001

expressを使用

いよいよ本題です。
まずは必要なライブラリをインストールします。

npm install express serverless-http

serverless/src/functions/express/handler.tsにapiという名前のhandlerを作成しました

handler.ts
import * as serverless from 'serverless-http';
import * as express from 'express';
import { APIGatewayProxyEvent, APIGatewayProxyEventV2, Context } from 'aws-lambda';
import cors from 'cors';

import { router } from '../../routers';

const app = express();

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/api/v1', router);

const handlers = serverless(app);
export const api = async (event: APIGatewayProxyEvent | APIGatewayProxyEventV2, context: Context) => {
  const result = await handlers(event, context);
  return { body: result.body };
};

handlersを作成するところまでは、普段のexpressの使い方と同じです。
だからexpressを使っているアプリをserverlessに置き換えようといった時には、そのままコピーして使うことができます。

serverless.tsのfunctionsに指定するための関数を、serverless/src/functions/express/index.tsに作成します。

index.ts
export const app = {
  handler: `src/functions/express/handler.api`,
  events: [
    {
      http: {
        method: 'ANY',
        path: '{proxy+}',
      },
    },
  ],
};

methodをANYにすることでGETでもPOSTでもPUTでも受け入れることができるようになります。
handlerに src/functions/express/handler.api が指定されています。これは、src/functions/express/handler.tsのapiという関数をhandlerとして登録するということです。apiという関数は先ほど作成したものです。忘れた方は少し上に戻って確認してみてください。

そしてこのappをserverless.tsでimportし、functionsに追加します。

functions: { app },

URLを変える

何も設定をしないでserverlessアプリを起動すると、エンドポイントが http://localhost:4001/dev になってしまいます。このdevというのはstage名です。serverlessではstageを指定できるのですが、何も指定しないとdevになります。

本番環境のエンドポイントのURLに「dev」という文字があるのも変なのでこれを変えようと思います。
まずserverless.tsのcustomオブジェクトにdefaultStageを追加します。

custom: {
  webpack: {
    webpackConfig: './webpack.config.js',
    includeModules: true,
  },
  defaultStage: 好きな文字列,
},

そしてserverless.tsのproviderオブジェクトにstageを追加します。

provider: {
    name: 'aws',
    region: 'ap-northeast-1',
    runtime: 'nodejs14.x',
    stage: '${self:custom.defaultStage}',
    ...
}

こうすることでエンドポイントが http://localhost:4001/指定した文字列 に変わります。
ちなみに僕は、proxyという文字列を指定しました。

まとめ

以上、ServerlessFramework, express, TypeScriptを一緒に使う方法をまとめました。
serverless.ymlで設定を書く方法は見つかりやすいのですが、serverless.tsに書く方法は見つかりづらいんですよね。。

この記事がserverless.tsに設定を書いている人のお役に立てれば幸いです。

Discussion