Serverless Framework V4でビルドしたコードの実行時にエラーが発生した時の対処法
こんにちは。株式会社シータグCTOの @y_okady です。
シータグでは社内業務の自動化にAWS Lambda + Serverless Framework V4を使用しています。従来はJavaScriptでLambda関数を記述していましたが、つい先日シータグ社内の社内標準プログラミング言語にTypeScriptを採用したことを受けて、TypeScriptへの移行を実施しました。
Serverless Framework V4ではLambda利用時にTypeScriptがネイティブでサポートされ、プラグインなしでTypeScriptファイルを扱うことができるようになりました。
これは、Serverless Framework V4に esbuild
が組み込まれたことによって実現されており、TypeScriptでLambda関数を記述したらデフォルトで自動ビルドされるそうです。
実際に試してみたところ、ビルド環境を整備しなくてもTypeScriptを扱うことができるのはとても便利でした。一方で、依存関係によってビルドしたコードの実行時にエラーが発生する場合がありました。
この記事では、Serverless Framework V4 TypeScriptネイティブサポートの使い方と、ビルドしたコードの実行時にエラーが発生した時の対処法をご紹介します。
サンプルファイル
以下のファイルを用いて動作を確認してみます。今回はESMで出力したいので package.json
の type
に module
を指定しています。
{
"main": "test.js",
"type": "module",
"devDependencies": {
"serverless": "^4.14.3"
},
"dependencies": {
"date-fns": "^4.1.0"
}
}
service: ctag
provider:
name: aws
runtime: nodejs22.x
region: ap-northeast-1
functions:
test:
handler: test.default
events:
- schedule: cron(0 0 * * ? *) # 毎日JST9:00
import { format } from "date-fns";
export default () => {
console.log(format(new Date(), "現在 HH 時 mm 分 ss 秒 (O) です"));
};
コマンドを試してみる
ローカルでの関数実行
$ npx sls invoke local --function test
現在 16 時 02 分 54 秒 (GMT+9) です
TypeScriptやビルドの設定をしなくても、sls invoke local
コマンドを実行するだけでローカルで関数を実行できました。
Lambdaへのデプロイ
$ npx sls deploy
Lambdaへのデプロイも、TypeScriptやビルドの設定は不要です。
Lambda関数の実行
$ npx sls invoke --function test --log
null
----------------------
START - 10151cdc-56be-4b3a-8ba4-f4ccc9c96252 - Version: $LATEST
2025-05-20 16:14:27.323 - 10151cdc-56be-4b3a-8ba4-f4ccc9c96252 -
INFO 現在 7 時 14 分 27 秒 (GMT+0) です
REPORT - 10151cdc-56be-4b3a-8ba4-f4ccc9c96252
Duration: 7.63 ms Billed Duration: 8 ms Memory Size: 1024 MB Max Memory Used: 83 MB Init Duration: 150.83 ms
定期実行関数であっても、sls invoke
コマンドまたはAWS マネジメントコンソールで即時実行可能です。
axios
を利用)
Lambda関数の実行時にエラーが発生するケースその1(test.ts
に import axios from "axios";
と console.log(axios);
を追記し、Lambdaにデプロイして関数を実行するとエラーが発生しました。
$ npm install --save axios
$ npx sls deploy && npx sls invoke -f test
{
"errorType": "Error",
"errorMessage": "Dynamic require of \"util\" is not supported",
"trace": [
"Error: Dynamic require of \"util\" is not supported",
" at file:///var/task/test.js:11:9",
" at node_modules/combined-stream/lib/combined_stream.js (/node_modules/combined-stream/lib/combined_stream.js:1:12)",
" at __require2 (file:///var/task/test.js:14:50)",
" at node_modules/form-data/lib/form_data.js (/node_modules/form-data/lib/form_data.js:1:22)",
" at __require2 (file:///var/task/test.js:14:50)",
" at <anonymous> (/node_modules/axios/lib/platform/node/classes/FormData.js:1:22)",
" at ModuleJob.run (node:internal/modules/esm/module_job:271:25)",
" at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:578:26)",
" at async _tryAwaitImport (file:///var/runtime/index.mjs:1008:16)",
" at async _tryRequire (file:///var/runtime/index.mjs:1057:37)"
]
}
ビルドされたファイルが保存される .serverless/build
ディレクトリ以下にある test.js
を確認すると以下の記述がありました。require
が undefined
のためにエラーが発生しているようです。
(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
axios
の内部で require
が利用されているにも関わらず、ESMのコードを生成しようとしてエラーになっているものと思われます。そこで、package.json
の type
を commonjs
に変更することで正常に実行することができました。
@kintone/rest-api-client
を利用)
Lambda関数の実行時にエラーが発生するケースその2(同じように test.ts
に import { KintoneRestAPIClient } from "@kintone/rest-api-client";
と console.log(KintoneRestAPIClient);
を追記し、Lambdaにデプロイして関数を実行するとエラーが発生しました。
$ npm install --save @kintone/rest-api-client
$ npx sls deploy && npx sls invoke -f test
{
"errorType": "Error",
"errorMessage": "require() of ES Module /var/task/test.js from /var/task/test.js not supported.\nInstead change the require of /var/task/test.js in /var/task/test.js to a dynamic import() which is available in all CommonJS modules.",
"trace": [
"Error [ERR_REQUIRE_ESM]: require() of ES Module /var/task/test.js from /var/task/test.js not supported.",
"Instead change the require of /var/task/test.js in /var/task/test.js to a dynamic import() which is available in all CommonJS modules.",
" at TracingChannel.traceSync (node:diagnostics_channel:322:14)",
" at file:///var/task/test.js:1578:5"
]
}
.serverless/build/test.js
を確認すると以下の記述がありました。module.createRequire
が返す関数の内部で require
が利用されているようです。
import module from "module";
var require2 = module.createRequire(import.meta.url);
var {
KintoneRestAPIClient,
KintoneAbortSearchError,
KintoneAllRecordsError,
KintoneRestAPIError
} = require2(".");
それならばと package.json
の type
を commonjs
に変更したところ、今後は別のエラーが発生しました。
$ npx sls deploy && npx sls invoke -f test
{
"errorType": "TypeError",
"errorMessage": "The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received undefined",
"trace": [
"TypeError [ERR_INVALID_ARG_VALUE]: The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received undefined",
" at Function.createRequire (node:internal/modules/cjs/loader:1777:11)",
" at Object.<anonymous> (/node_modules/@kintone/rest-api-client/index.mjs:3:24)",
" at Module._compile (node:internal/modules/cjs/loader:1554:14)",
" at Object..js (node:internal/modules/cjs/loader:1706:10)",
" at Module.load (node:internal/modules/cjs/loader:1289:32)",
" at Function._load (node:internal/modules/cjs/loader:1108:12)",
" at TracingChannel.traceSync (node:diagnostics_channel:322:14)",
" at wrapModuleLoad (node:internal/modules/cjs/loader:220:24)",
" at Module.require (node:internal/modules/cjs/loader:1311:12)",
" at require (node:internal/modules/helpers:136:16)"
]
}
.serverless/build/test.js
を確認すると以下の記述がありました。import_meta.url
を使っているにも関わらず、import_meta
が空オブジェクトになってしまっています。
var import_meta = {};
var require2 = import_module.default.createRequire(import_meta.url);
以下の記事によると、import.meta.url
が含まれるコードを esbuild
でCJSに変換するとこのような現象が起こるそうです。
axios
のケースではESMをCJSに変更することでエラーを回避できましたが、@kintone/rest-api-client
のケースではESMでもCJSでもエラーが発生してしまいます。こういう時は esbuild
のビルドオプション external
を利用します。
serverless.yml
に以下を追記してデプロイします。
build:
esbuild:
external:
- "@kintone/rest-api-client"
すると、.serverless/build/test.js
に @kintone/rest-api-client
はバンドルされず、以下のように import
文がそのまま残ります。また、.serverless/build/node_modules
が新たに作成され、その中に @kintone/rest-api-client
の依存関係が格納されていることが確認できます。これで無事に正常に実行することができました。
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
まとめ
Serverless Framework V4でビルドしたコードの実行時にエラーが発生したら、次のことを試してみてください!
-
package.json
のtype
を変更する -
esbuild
のビルドオプションexternal
を利用する
Discussion