如何使用 TypeScript 编写的 API 网关在 VS Code 中调试本地 AWS Lambda 函数?
How to debug in VS Code a local AWS Lambda function with API Gateway written in TypeScript?
我们即将开始使用 Lambda 函数。
我们有必须使用 TypeScript 的技术限制。
当从 Postman 调用相关端点时,我希望能够在 VS Code 中调试我的 ts 文件。
这样,我们就有了如下的开发环境:
- Windows 10
- Docker for Windows(使用 Hyper-V 而不是 WSL 2)
- 打字稿 4.1
- 节点 12.17
- SAM CLI 1.13.2
我使用 sam init
和 Hello World 模板来生成初始文件夹结构。
我对其进行了增强(主要基于 this article)以使用 TypeScript。
文件夹结构
.
├── template.yaml
├── .aws-sam
├── .vscode
| └── launch.json
├── events
├── hello-world
| ├── dist
| ├── app.js
| └── app.js.map
| ├── src
| └── app.ts
| ├── package.json
| └── tsconfig.json
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
LambdaWithApiGateWayDebug
Sample SAM Template for LambdaWithApiGateWayDebug
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/dist
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"sourceRoot": "./src",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
package.json
{
"name": "hello_world",
"version": "1.0.0",
"description": "hello world sample for NodeJS",
"main": "app.js",
"repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
"scripts": {
"compile": "tsc",
"start": "sam local start-api -t ../template.yaml -p 5000 -d 5678"
},
"dependencies": {
"@types/aws-lambda": "^8.10.64",
"@types/node": "^14.14.10",
"aws-sdk": "^2.805.0",
"source-map-support": "^0.5.19",
"typescript": "^4.1.2"
}
}
app.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
}
}
app.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lambdaHandler = void 0;
const lambdaHandler = async (event) => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
};
};
exports.lambdaHandler = lambdaHandler;
//# sourceMappingURL=app.js.map
app.js.map
{"version":3,"file":"app.js","sourceRoot":"./src/","sources":["app.ts"],"names":[],"mappings":";;;AAEO,MAAM,aAAa,GAAG,KAAK,EAAE,KAA2B,EAAkC,EAAE;IAC/F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5D,OAAO;QACL,UAAU,EAAE,GAAG;QACf,IAAI,EAAE,YAAY,OAAO,EAAE;KAC5B,CAAA;AACL,CAAC,CAAA;AANY,QAAA,aAAa,iBAMzB"}
launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "attach Program",
"port": 5678,
"address": "localhost",
"localRoot": "${workspaceFolder}/hello-world/dist",
"remoteRoot": "/var/task",
"protocol": "inspector",
"sourceMaps": true,
"smartStep": true,
"outFiles": ["${workspaceFolder}/hello-world/dist"]
}
]
}
如您所见:
- 我的 lambda 函数定义在
hello-world/src/app.ts
- 它符合 commonJs 和 ES2020 目标
hello-world/dist/app.js
with sourcemap
- 模板通过
localhost:5000/hello
端点公开位于 hello-world/dist
下的处理程序
- 调试器正在侦听端口 5678
所以,当我调用 npm run start
时,它会打印以下输出:
> hello_world@1.0.0 start C:\temp\AWS\LambdaWithApiGateWayDebug\hello-world
> sam local start-api -t ../template.yaml -p 5000 -d 5678
Mounting HelloWorldFunction at http://127.0.0.1:5000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-12-08 11:40:48 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
当我通过 Postman 向端点发出请求时,控制台会扩展为以下文本:
Mounting C:\temp\AWS\LambdaWithApiGateWayDebug\hello-world\dist as /var/task:ro,delegated inside runtime container
START RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13 Version: $LATEST
Debugger listening on ws://0.0.0.0:5678/d6702717-f291-42cd-8056-22b9f029f4dd
For help, see: https://nodejs.org/en/docs/inspector
当我将我的 VS 代码附加到节点进程时,我只能调试 app.js
而不是 app.ts
。
控制台日志结束:
Debugger attached.
END RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13
REPORT RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13 Init Duration: 0.12 ms Duration: 7064.19 ms Billed Duration: 7100 ms Memory Size: 128 MB Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2020-12-08 11:40:58 127.0.0.1 - - [08/Dec/2020 11:40:58] "GET /hello HTTP/1.1" 200 -
问题
我应该更改什么才能调试我的 app.ts
而不是 app.js
?
您的 Lambda 运行时是 JavaScript,但您使用 TypeScript 编写代码。当您使用 TypeScript 时,编译器会在后台将您的代码转换为 JavaScript 可调试代码。
换句话说,你在 TS 中编写你的 lambda,然后编译器将其转换为 JS,最后是执行并可以调试的代码。
我写了一篇解释如何创建、调用和调试 TypeScript lambda 函数的中篇文章。
请查看以下内容以了解更多详情 link.
要求
1-创建一个nodejs12x;
创建一个nodejs12x lambda;
安装打字稿:npm i -g typescript
初始化打字稿:tsc --init
(它将创建 tsconfig.json 文件)
用以下代码替换创建的tsconfig.json文件:
{
"compilerOptions": {
"module": "CommonJS",
"target": "ES2017",
"noImplicitAny": true,
"preserveConstEnums": true,
"outDir": "./built",
"sourceMap": true
},
"include": ["handler/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
删除app.js个文件;
在处理程序文件夹内的 TypeScript 代码中创建您的 lambda(您需要创建它):
import {
APIGatewayProxyEvent,
APIGatewayProxyResult
} from "aws-lambda";
export const lambdaHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
}
}
适配template.yaml。更改 lambda 的 CodeUri 路径:CodeUri: hello-world/built
安装需要的节点包:npm install typescript @types/aws-lambda @types/node -save-dev
包裹json:
{
"name": "typescript_lambda",
"version": "1.0.0",
"description": "hello world sample for TypeScript",
"main": "app.js",
"repository": "https://github.com/jafreitas90/AWS",
"author": "Jorge Freitas",
"license": "JorgeFreitas Ltd :)",
"dependencies": {
},
"scripts": {
"compile": "tsc"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.71",
"@types/node": "^14.14.22",
"aws-sdk": "^2.815.0",
"typescript": "^4.1.3"
}
}
npm 安装
npm 运行 编译
在您的 lambda 函数(TypeScript 代码)中设置一个断点。在左侧面板 select 调试然后开始调试(顶部的绿色按钮)。就是这样:)
项目结构
事实证明我犯了两个小错误:
sourceRoot
我在 tsconfig.json
中设置了 sourceRoot
,在这种情况下这是不必要的。 include
就够了:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
// "sourceRoot": "./src",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
断点
我设置了两个断点:一个在 app.js
内,另一个在 app.ts
.
内
事实证明,如果 app.js
确实有一个断点,那么另一个断点将不会被触发。
所以在我从 app.js
中删除断点后,调试器在 app.ts
.
内停止
我们即将开始使用 Lambda 函数。
我们有必须使用 TypeScript 的技术限制。
当从 Postman 调用相关端点时,我希望能够在 VS Code 中调试我的 ts 文件。
这样,我们就有了如下的开发环境:
- Windows 10
- Docker for Windows(使用 Hyper-V 而不是 WSL 2)
- 打字稿 4.1
- 节点 12.17
- SAM CLI 1.13.2
我使用 sam init
和 Hello World 模板来生成初始文件夹结构。
我对其进行了增强(主要基于 this article)以使用 TypeScript。
文件夹结构
.
├── template.yaml
├── .aws-sam
├── .vscode
| └── launch.json
├── events
├── hello-world
| ├── dist
| ├── app.js
| └── app.js.map
| ├── src
| └── app.ts
| ├── package.json
| └── tsconfig.json
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
LambdaWithApiGateWayDebug
Sample SAM Template for LambdaWithApiGateWayDebug
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/dist
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"sourceRoot": "./src",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
package.json
{
"name": "hello_world",
"version": "1.0.0",
"description": "hello world sample for NodeJS",
"main": "app.js",
"repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
"scripts": {
"compile": "tsc",
"start": "sam local start-api -t ../template.yaml -p 5000 -d 5678"
},
"dependencies": {
"@types/aws-lambda": "^8.10.64",
"@types/node": "^14.14.10",
"aws-sdk": "^2.805.0",
"source-map-support": "^0.5.19",
"typescript": "^4.1.2"
}
}
app.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
}
}
app.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lambdaHandler = void 0;
const lambdaHandler = async (event) => {
const queries = JSON.stringify(event.queryStringParameters);
return {
statusCode: 200,
body: `Queries: ${queries}`
};
};
exports.lambdaHandler = lambdaHandler;
//# sourceMappingURL=app.js.map
app.js.map
{"version":3,"file":"app.js","sourceRoot":"./src/","sources":["app.ts"],"names":[],"mappings":";;;AAEO,MAAM,aAAa,GAAG,KAAK,EAAE,KAA2B,EAAkC,EAAE;IAC/F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5D,OAAO;QACL,UAAU,EAAE,GAAG;QACf,IAAI,EAAE,YAAY,OAAO,EAAE;KAC5B,CAAA;AACL,CAAC,CAAA;AANY,QAAA,aAAa,iBAMzB"}
launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "attach Program",
"port": 5678,
"address": "localhost",
"localRoot": "${workspaceFolder}/hello-world/dist",
"remoteRoot": "/var/task",
"protocol": "inspector",
"sourceMaps": true,
"smartStep": true,
"outFiles": ["${workspaceFolder}/hello-world/dist"]
}
]
}
如您所见:
- 我的 lambda 函数定义在
hello-world/src/app.ts
- 它符合 commonJs 和 ES2020 目标
hello-world/dist/app.js
with sourcemap - 模板通过
localhost:5000/hello
端点公开位于hello-world/dist
下的处理程序 - 调试器正在侦听端口 5678
所以,当我调用 npm run start
时,它会打印以下输出:
> hello_world@1.0.0 start C:\temp\AWS\LambdaWithApiGateWayDebug\hello-world
> sam local start-api -t ../template.yaml -p 5000 -d 5678
Mounting HelloWorldFunction at http://127.0.0.1:5000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-12-08 11:40:48 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
当我通过 Postman 向端点发出请求时,控制台会扩展为以下文本:
Mounting C:\temp\AWS\LambdaWithApiGateWayDebug\hello-world\dist as /var/task:ro,delegated inside runtime container
START RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13 Version: $LATEST
Debugger listening on ws://0.0.0.0:5678/d6702717-f291-42cd-8056-22b9f029f4dd
For help, see: https://nodejs.org/en/docs/inspector
当我将我的 VS 代码附加到节点进程时,我只能调试 app.js
而不是 app.ts
。
控制台日志结束:
Debugger attached.
END RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13
REPORT RequestId: 04d884cf-fa96-4d58-b41c-e4196e12de13 Init Duration: 0.12 ms Duration: 7064.19 ms Billed Duration: 7100 ms Memory Size: 128 MB Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2020-12-08 11:40:58 127.0.0.1 - - [08/Dec/2020 11:40:58] "GET /hello HTTP/1.1" 200 -
问题
我应该更改什么才能调试我的 app.ts
而不是 app.js
?
您的 Lambda 运行时是 JavaScript,但您使用 TypeScript 编写代码。当您使用 TypeScript 时,编译器会在后台将您的代码转换为 JavaScript 可调试代码。
换句话说,你在 TS 中编写你的 lambda,然后编译器将其转换为 JS,最后是执行并可以调试的代码。
我写了一篇解释如何创建、调用和调试 TypeScript lambda 函数的中篇文章。 请查看以下内容以了解更多详情 link.
要求
1-创建一个nodejs12x;
创建一个nodejs12x lambda;
安装打字稿:
npm i -g typescript
初始化打字稿:
tsc --init
(它将创建 tsconfig.json 文件)用以下代码替换创建的tsconfig.json文件:
{ "compilerOptions": { "module": "CommonJS", "target": "ES2017", "noImplicitAny": true, "preserveConstEnums": true, "outDir": "./built", "sourceMap": true }, "include": ["handler/**/*"], "exclude": ["node_modules", "**/*.spec.ts"] }
删除app.js个文件;
在处理程序文件夹内的 TypeScript 代码中创建您的 lambda(您需要创建它):
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda"; export const lambdaHandler = async ( event: APIGatewayProxyEvent ): Promise<APIGatewayProxyResult> => { const queries = JSON.stringify(event.queryStringParameters); return { statusCode: 200, body: `Queries: ${queries}` } }
适配template.yaml。更改 lambda 的 CodeUri 路径:
CodeUri: hello-world/built
安装需要的节点包:
npm install typescript @types/aws-lambda @types/node -save-dev
包裹json:
{ "name": "typescript_lambda", "version": "1.0.0", "description": "hello world sample for TypeScript", "main": "app.js", "repository": "https://github.com/jafreitas90/AWS", "author": "Jorge Freitas", "license": "JorgeFreitas Ltd :)", "dependencies": { }, "scripts": { "compile": "tsc" }, "devDependencies": { "@types/aws-lambda": "^8.10.71", "@types/node": "^14.14.22", "aws-sdk": "^2.815.0", "typescript": "^4.1.3" } }
npm 安装
npm 运行 编译
在您的 lambda 函数(TypeScript 代码)中设置一个断点。在左侧面板 select 调试然后开始调试(顶部的绿色按钮)。就是这样:)
项目结构
事实证明我犯了两个小错误:
sourceRoot
我在 tsconfig.json
中设置了 sourceRoot
,在这种情况下这是不必要的。 include
就够了:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
// "sourceRoot": "./src",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
断点
我设置了两个断点:一个在 app.js
内,另一个在 app.ts
.
内
事实证明,如果 app.js
确实有一个断点,那么另一个断点将不会被触发。
所以在我从 app.js
中删除断点后,调试器在 app.ts
.