はじめに
私はExpressアプリケーションをコストが低いサーバレス(API Gateway/Lambda/DynamoDB)にデプロイしようと考えました。
パブリッククラウドに慣れていない+従量課金制に恐怖があったので、できる限りデプロイ前にローカルでテストできないか調査し、Serverless Framework+プラグインを使用してローカルでテストできる環境を構築することにしました。
Serverless Framework+Serverless-offline+Expressでローカルにサーバレス環境を構築したところ、ルートディレクトリにアクセスしたときに、cannot get null というエラーが発生しました。このエラー解決に時間がかかったので、記事にしておこうと思いました。
この記事の対象者
- 同じエラーが出て困っている人
- Serverless Frameworkを使っている人
- サーバレスをローカル環境に構築したい人
実行環境
- OS:Windows 11 Home 21H2
- 言語
- Node.js
- version: 16.11.1
- npm
- version: 8.0.0
- Node.js
- 使用しているライブラリ・フレームワーク
- Express
- version: 4.18.1
- serverless
- version: ^3.24.1
- @vendia/serverless-express
- version: ^4.10.1
- serverless-offline
- version: ^11.3.0
- Express
前提知識
Serverless Frameworkについて
- Serverless Applicationを構成管理デプロイするためのツール
-
IaC(インフラのコード化)、デプロイが楽になる、Serverlessアプリケーションの管理がしやすい、プラグインが豊富でローカル環境の構築が楽にできる。
- 類似サービス:AWS SAM
Serverless-offlineについて
- API Gatewayをローカルで動かすServerless Frameworkの拡張機能
- 完全にAPI Gatewayを再現しているわけではない
Expressについて
- Node.jsにおけるWebフレームワーク
- 基本的なミドルウェアを備え、拡張モジュールが豊富
- 完全にAPI Gatewayを再現しているわけではない
Serverless-expressについて
- API Gateway + lambda上でExpressアプリケーションを動かすことができるライブラリ
- API Gateway→lambdaからくるイベントオブジェクトの変換の他、API Gateway + lambdaとの連携をよしなにやってくれるライブラリ
- 完全にAPI Gatewayを再現しているわけではない
エラーの詳細
やりたかったこと
- AWS Serverless環境(lambda/API Gateway/DynamoDB)をローカル環境に疑似的に構築し、デプロイ前にテストを行う。
- serverless-expressを使ってExpressとLambdaを接続する。
- Serverless-offline→Serverless-offline→Expressの順にリクエストを送り、ルーティング処理が行われる。
発生したエラー(上手くいかなかったこと)
Serverless-offlineとServerless-expressとの接続をする際にエラーが発生。
http://localhost:3000/dev/
にアクセスしても、cannot get nullが返ってきてしまう。
↓ http://localhost:3000/dev/
にアクセスした結果
調査したこと、思考プロセス
2 Serverless-offlineを使わないで、Expressの開発用サーバーでルーティングできるか試す
- 問題なくルーティングできた。→Express側のルーティングの問題ではない。
3 Serverless-offlineで開発サーバーを立ち上げ、他のルーティングが動くか確認する
- 今までルートディレクトリ(
/
)にアクセスして表示されなかった -
/index
のルートを追加してテスト→表示された - →ルートディレクトリのときだけ起こるエラー
4「Serverless-offline root directory cannot get null」で検索
- Google検索、GitHub Issueで検索
- 参考記事を発見
調査結果、エラーの発生原因
※私がまだ未熟なエンジニアですので以下の推測が間違っている場合があります。
もし間違っている箇所があればコメントで指摘をしていただけると幸いです。
-
Serverless-offlineがServerless-expressにプロキシする際に、root directoryだけスラッシュを入れた状態でプロキシしてしまうと思われる(ちゃんとソースコード見てないので推測です)。
-
http://localhost:3000/dev/
というURLでプロキシしてしまい、http://localhost:3000/dev//
とスラッシュが重複してしまう。- serverless-offlineとserverless-express間のソースコードを見れば、原因がわかりそうではありますが。今回は割愛します。
エラーの解決法
解決方法①
- ルートディレクトリを使用しない
-
今回のエラーはルートディレクトリときのみ起きるエラーなのでそもそもExpress側のルーティングでルートディレクトリを使用しないようにする。
-
例 /indexにアクセスするようにする
// ルートディレクトリを使用しない // app.get("/", (req, res) => ()) //代わりに/indexのようなルーティングにする app.get("/index", (req, res) => ())
-
解決方法②
- ルートディレクトリを使用したいケースも存在するため、その場合はServerless.ymlにサンプルのコードを追記する。
Serverless.yml
- Serverless Frameworkでサーバレスのインフラをコードで書くファイル。
- ここにAWSの各リソースの設定やローカル環境時の設定等を書く。
- インフラをコードで表した設計図
provider:
name: aws
region: ap-northeast-1
stage: dev
runtime: nodejs14.x
~~~~~~~~~~~~~~~~~~~~~~~ 以下省略
// 以下を追記する。
custom:
serverless-offline:
noPrependStageInUrl: true
- 上記を書くとServerless-offlineで実行するときはstage名(/dev)をURLに含まめない
- Serverless-offline側でstage名を追加するときに
/
を追加し、dev/
としていることがスラッシュが重複する原因だと思われます。
- Serverless-offline側でstage名を追加するときに
- こうした場合、下記URLでアクセス可能
http://localhost:3000/
- Expressの開発サーバーを使うのと同じになります。
- 今回の設定はローカルで実行するときのみ有効なので、デプロイするときはstage名(/dev)は追加されます。
- 参考にしたGithub Issues
serverless-http 2.0.0 with express · Issue #86 · dougmoscrop/serverless-http
Serverless-offline: route not found · Issue #88 · dherault/serverless-offline
おわりに
今回はServerlessをローカル環境で再現してるときに遭遇したエラーの解決法について書かせていただきました。実際の原因と詳しくは特定できていないので、もしご存じの方がいましたらご連絡ください。
参考にしたサイト・動画等
-
参考にしたGithub Issues
serverless-http 2.0.0 with express · Issue #86 · dougmoscrop/serverless-http
Serverless-offline: route not found · Issue #88 · dherault/serverless-offline -
各ライブラリ、拡張機能の公式URL
- Serverless Framework
Serverless: Develop & Monitor Apps On AWS Lambda - Serverless-offline
GitHub - dherault/serverless-offline: Emulate AWS λ and API Gateway locally when developing your Serverless project - Serverless-express
GitHub - vendia/serverless-express: Run Node.js web applications and APIs using existing application frameworks on AWS #serverless technologies such as Lambda, API Gateway, Lambda@Edge, and ALB.
- Serverless Framework