【问题标题】:How to debug in VS Code a local AWS Lambda function with API Gateway written in TypeScript?如何使用用 TypeScript 编写的 API Gateway 在 VS Code 中调试本地 AWS Lambda 函数?
【发布时间】:2021-03-19 16:41:11
【问题描述】:

我们即将开始使用 Lambda 函数。
我们有必须使用 TypeScript 的技术限制。
当从 Postman 调用相关端点时,我希望能够在 VS Code 中调试我的 ts 文件。

所以,我们有以下开发环境:

  • Windows 10
  • Docker for Windows(使用 Hyper-V 而非 WSL 2)
  • TypeScript 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 和 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

【问题讨论】:

  • 我也有同样的问题,直到现在我只能调试javascript代码,试图调试找出解决方案。我会告诉你的

标签: node.js typescript amazon-web-services aws-lambda aws-sam-cli


【解决方案1】:

我写了一篇中篇文章,解释如何创建、调用和调试 TypeScript lambda 函数。 更多详情请查看以下link

要求

1- 创建一个nodejs12x;

  1. 创建一个nodejs12x lambda;

  2. 安装 TypeScript:npm i -g typescript

  3. 初始化打字稿:tsc --init(它将创建 tsconfig.json 文件)

  4. 将创建的 tsconfig.json 文件替换为以下代码:

    {
    "compilerOptions": {
    "module": "CommonJS",
    "target": "ES2017",
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "outDir": "./built",
    "sourceMap": true
    },
    "include": ["handler/**/*"],
    "exclude": ["node_modules", "**/*.spec.ts"]
    }
    
  5. 删除 app.js 文件;

  6. 在处理程序文件夹内的 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}`
      }
    }
    
  7. 调整 template.yaml。更改 lambda 的 CodeUri 路径:CodeUri: hello-world/built

  8. 安装需要的节点包:npm install typescript @types/aws-lambda @types/node -save-dev

  9. 包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"
    }
    }
    
  10. npm 安装

  11. npm 运行编译

  12. 在 lambda 函数中设置断点(TypeScript 代码)。在左侧面板上选择调试,然后选择开始调试(顶部的绿色按钮)。就是这样:)

  13. 项目结构

Please find more details in my tutorial.

source code

【讨论】:

  • 我已经尝试过您提出的解决方案,您在文章中已经描述过。您的示例在我的本地机器上按预期工作(我可以调试app.ts),但我无法应用于我的项目(还)。我将尝试其他几种方法来实现它。我会及时通知您。
  • 事实证明我犯了两个小错误。 (如果您对细节感兴趣,请阅读我的帖子)。现在我可以附加节点检查器而无需使用aws-sam。这比您的方法更灵活一些,因为它对特定的 lambda 或端点(及其有效负载)并不严格。因此,使用不同的有效负载多次测试它或简单地调用不同的 api 端点更容易,而无需启动新的调试器。
  • 正如您在文章末尾看到的那样,我提出了两种解决方案:使用 aws-sam 和使用“附加到 SAM CLI”的另一种解决方案;所以如果你不想的话,你不需要使用 aws-sam
【解决方案2】:

事实证明我犯了两个小错误:

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 内停止。

【讨论】:

    【解决方案3】:

    您的 Lambda 运行时是 JavaScript,但您使用 TypeScript 编写代码。当您使用 TypeScript 时,编译器会在后台将您的代码转换为可调试代码的 JavaScript。

    换句话说,你在 TS 中编写你的 lambda,编译器然后将其转换为 JS,最后是被执行并可以调试的代码。

    【讨论】:

    • 是的,确实如此,但使用 sourceMap 调试器应该能够将 JS 代码的给定行追溯到 TS 代码的相关行。这就是我在 launch.json 中指定 "sourceMaps": true 标志的原因。
    • 嗯,我明白你的意思了。我们最终要做的是在 VS Code 中调试 JS 代码,然后从那里我们回到 TS 文件进行更改。我猜这是你想要避免的?
    猜你喜欢
    • 2018-08-28
    • 2017-12-07
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 2017-01-07
    • 2020-01-01
    • 2020-08-21
    • 1970-01-01
    相关资源
    最近更新 更多