尝试使用aws-nodejs-typescript模板

首先

在业务中,我经常使用ServerlessFramework,当使用typescript的模板时,serverless.yml变为serverless.ts,并且构建工具从webpack改为esbuild,这一点让我感到困惑。因此,我想分享一下这些变化,并总结一下。

Serverless Framework是什么?

Serverless Framework是什么?

根据https://github.com/serverless/serverless,Serverless Framework是一个什么?

无服务器框架 – 在AWS Lambda和其他新一代云服务上构建应用程序,自动扩展并且只在运行时收费。这降低了运行和操作应用程序的总成本,使您能够更多地构建和少量管理。

听说如此。因为用英语听起来很费劲,所以转成日语翻译一下(来自DeepL翻译)。

在AWS Lambda和其他下一代云服务上构建应用程序,实现自动扩展,并仅在运行时收费,这样可以降低应用程序的运行和运维成本,实现更多的构建和更少的管理。

简而言之,Serverless Framework 是一个可以利用 AWS Lambda 和 Amazon API Gateway 等服务来快速构建和部署无服务器应用的框架。

准备工作

实际运行环境如下所示。
关于serverless和awscli,我们使用brew命令进行安装。

❯ node -v
v17.3.0

❯ npm --version
8.3.0

❯ serverless --version
Framework Core: 2.71.0
Plugin: 5.5.3
SDK: 4.3.0
Components: 3.18.1

❯ aws --version
aws-cli/2.4.11 Python/3.9.9 Darwin/20.6.0 source/x86_64 prompt/off

通过执行aws configure命令,设置AWS的认证信息,按照提示进行操作即可。

$ aws configure
AWS Access Key ID [None]: your_access_key_id
AWS Secret Access Key [None]: your_secret_access_key
Default region name [None]:
Default output format [None]:

如果没有访问密钥ID和秘密访问密钥,
请在设置的基本 – AWS命令行接口的访问密钥ID和秘密访问密钥部分中,按照所述的创建步骤进行创建,并进行注册。

请参考以下链接

    • 設定の基本 – AWS Command Line Interface

 

    Serverless Framework – AWS Lambda Guide – Credentials

创建项目

那么,让我们立即开始创建项目。

❯ sls create --template aws-nodejs-typescript --path nodejs-typescript-test
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/**/nodejs-typescript-test"
Serverless: Successfully generated boilerplate for template: "aws-nodejs-typescript"

以下为由模板生成的代码。

稍微引起了我的兴趣点1。

根据使用模板创建的README.md文件,目录结构似乎是这样的。

.
├── src
│   ├── functions               # Lambda configuration and source code folder
│   │   ├── hello
│   │   │   ├── handler.ts      # `Hello` lambda source code
│   │   │   ├── index.ts        # `Hello` lambda Serverless configuration
│   │   │   ├── mock.json       # `Hello` lambda input parameter, if any, for local invocation
│   │   │   └── schema.ts       # `Hello` lambda input event JSON-Schema
│   │   │
│   │   └── index.ts            # Import/export of all lambda configurations
│   │
│   └── libs                    # Lambda shared code
│       └── apiGateway.ts       # API Gateway specific helpers
│       └── handlerResolver.ts  # Sharable library for resolving lambda handlers
│       └── lambda.ts           # Lambda middleware
│
├── package.json
├── serverless.ts               # Serverless service file
├── tsconfig.json               # Typescript compiler configuration
├── tsconfig.paths.json         # Typescript paths
└── webpack.config.js           # Webpack configuration

然而,当查看serverless.ts时,可以看到plugins中有serverless-esbuild,这意味着构建工具使用的是esbuild。因此,最下面的webpack.config.js文件不会被生成。

根据releases的记录,确认版本为v2.60.0。

切换到在 AWS 上使用 esbuild 的 aws-nodejs-typescript

因此,根据某个源头,似乎在这个版本中有一些修改,但是 README.md 文件似乎没有改变。所以,暂时不用太担心这个问题,应该没什么大问题。

执行yarn安装

以下的步骤是执行 yarn install 命令来安装库。

cd nodejs-typescript-test/

❯ yarn
yarn install v1.22.17
info No lockfile found.
[1/5] ?  Validating package.json...
[2/5] ?  Resolving packages...
warning serverless > aws-sdk > uuid@3.3.2: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
warning serverless > @serverless/cli > @serverless/utils > uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
warning serverless > aws-sdk > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning serverless > @serverless/platform-client > querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning serverless > aws-sdk > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning serverless > @serverless/components > @serverless/platform-client-china > querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning serverless > json-refs > path-loader > superagent@3.8.3: Please upgrade to v7.0.2+ of superagent.  We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing.  See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>. Thanks to @shadowgate15, @spence-s, and @niftylettuce. Superagent is sponsored by Forward Email at <https://forwardemail.net>.
warning serverless > json-refs > path-loader > superagent > formidable@1.2.6: Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau
warning serverless > @serverless/components > @serverless/platform-client-china > @serverless/utils-china > kafka-node > uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
warning serverless > @serverless/components > @serverless/platform-client-china > @serverless/utils-china > @tencent-sdk/capi > request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
warning serverless > @serverless/components > @serverless/platform-client-china > @serverless/utils-china > @tencent-sdk/capi > request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
warning serverless > @serverless/components > @serverless/platform-client-china > @serverless/utils-china > @tencent-sdk/capi > request > uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
warning serverless > @serverless/components > @serverless/platform-client-china > @serverless/utils-china > @tencent-sdk/capi > request > har-validator@5.1.5: this library is no longer supported
[3/5] ?  Fetching packages...
[4/5] ?  Linking dependencies...
warning "serverless > @serverless/components > inquirer-autocomplete-prompt@1.4.0" has unmet peer dependency "inquirer@^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0".
warning " > serverless-esbuild@1.23.3" has unmet peer dependency "esbuild@>=0.8 <0.15".
[5/5] ?  Building fresh packages...
success Saved lockfile.
✨  Done in 137.15s.

有一点我稍微担心,点②。

生成的serverless.ts如下所示。

import type { AWS } from '@serverless/typescript';

import hello from '@functions/hello';

const serverlessConfiguration: AWS = {
  service: 'nodejs-typescript-test',
  frameworkVersion: '2',
  plugins: ['serverless-esbuild'],
  provider: {
    name: 'aws',
    runtime: 'nodejs14.x',
    apiGateway: {
      minimumCompressionSize: 1024,
      shouldStartNameWithService: true,
    },
    environment: {
      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
      NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000',
    },
    lambdaHashingVersion: '20201221',
  },
  // import the function via paths
  functions: { hello },
  package: { individually: true },
  custom: {
    esbuild: {
      bundle: true,
      minify: false,
      sourcemap: true,
      exclude: ['aws-sdk'],
      target: 'node14',
      define: { 'require.resolve': undefined },
      platform: 'node',
      concurrency: 10,
    },
  },
};

module.exports = serverlessConfiguration;

让我们将生成的 serverless.yml 与 aws-nodejs 模板进行比较,以确认在从 yml 到 ts 的转变中有哪些变化。

# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: aws-nodejs # NOTE: update this with your service name
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iam:
#    role:
#      statements:
#        - Effect: "Allow"
#          Action:
#            - "s3:ListBucket"
#          Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#        - Effect: "Allow"
#          Action:
#            - "s3:PutObject"
#          Resource:
#            Fn::Join:
#              - ""
#              - - "arn:aws:s3:::"
#                - "Ref" : "ServerlessDeploymentBucket"
#                - "/*"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  patterns:
#    - '!exclude-me.js'
#    - '!exclude-me-dir/**'
#    - include-me.js
#    - include-me-dir/**

functions:
  hello:
    handler: handler.hello
#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - httpApi:
#          path: /users/create
#          method: get
#      - websocket: $connect
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
#      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
#      - iot:
#          sql: "SELECT * FROM 'some_topic'"
#      - cloudwatchEvent:
#          event:
#            source:
#              - "aws.ec2"
#            detail-type:
#              - "EC2 Instance State-change Notification"
#            detail:
#              state:
#                - pending
#      - cloudwatchLog: '/aws/lambda/hello'
#      - cognitoUserPool:
#          pool: MyUserPool
#          trigger: PreSignUp
#      - alb:
#          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
#          priority: 1
#          conditions:
#            host: example.com
#            path: /hello

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"

我认为大致上可以掌握从yml到ts的写作方式,可以理解大致的感觉。我稍微担心的是,在yml中,

stage: ${opt:stage, 'dev'}

我在想,对于在ts中如何定义这个地方会有什么感觉。

我发现建立在 Serverless Framework `serverless.yaml` 基础上的 `serverless.ts`,通过这个人创建的 CLI,可以轻松进行转换操作。我也想试试用这个 CLI 进行转换,所以在 GitHub 上查看了一下大家是如何实现 serverless.ts 的(https://github.com/search?l=TypeScript&q=%22%24%7Bopt%3A+stage%2C+%27dev%27%7D%22&type=Code)。

stage: '${opt:stage, "dev"}'

看起来是这样实现的,嗯,我明白了。

功能

正好,我正在比较 serverless.yml 和 serverless.ts,所以让我们来看一下 functions 的配置。在 TypeScript 中,

import schema from './schema';
import { handlerPath } from '@libs/handlerResolver';

export default {
  handler: `${handlerPath(__dirname)}/handler.main`,
  events: [
    {
      http: {
        method: 'post',
        path: 'hello',
        request: {
          schemas: {
            'application/json': schema
          }
        }
      }
    }
  ]
}

在serverless.ts中记录了在serverless.yml中记录的配置。这样,责任被分离出来。

请参考以下链接

    • Serverless Framework Commands – AWS Lambda – Create

 

    Serverless Frameworkの`serverless.yaml`を`serverless.ts`に変換する | WP Kyoto

确认构成

让我们简要查看代码,以确认使用模板创建的服务如何运作。就像我们之前确认过的那样,在 serverless.ts 文件中。

import hello from '@functions/hello';

functions: { hello },

因为被定义为这样,所以我们来看一下“你好”。

└── hello
      ├── handler.ts      # `Hello` lambda source code
      ├── index.ts        # `Hello` lambda Serverless configuration
      ├── mock.json       # `Hello` lambda input parameter, if any, for local invocation
      └── schema.ts       # `Hello` lambda input event JSON-Schema

首先让我们再次查看 index.ts。

import schema from './schema';
import { handlerPath } from '@libs/handlerResolver';

export default {
  handler: `${handlerPath(__dirname)}/handler.main`,
  events: [
    {
      http: {
        method: 'post',
        path: 'hello',
        request: {
          schemas: {
            'application/json': schema
          }
        }
      }
    }
  ]
}

作为处理程序,函数被传递给了handler,并且事件被传递给了events,这些事件将触发函数的执行。

接下来,让我们看一下 handler.ts 文件。

import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/apiGateway';
import { formatJSONResponse } from '@libs/apiGateway';
import { middyfy } from '@libs/lambda';

import schema from './schema';

const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {
  return formatJSONResponse({
    message: `Hello ${event.body.name}, welcome to the exciting Serverless world!`,
    event,
  });
}

export const main = middyfy(hello);

刚才我们已经注册了,当有API请求时,会返回JSON响应。

根据上述内容,发送POST请求到路径’hello’将返回一个具有message和event属性的JSON。

接下来,我们来进行本地执行并进行确认。

本地执行

根据 README.md 中的说明,让我们在本地试着运行一下。

❯ yarn sls invoke local -f hello --path src/functions/hello/mock.json
yarn run v1.22.17
$ /Users/**/nodejs-typescript-test/node_modules/.bin/sls invoke local -f hello --path src/functions/hello/mock.json

 Error ---------------------------------------------------

  Error: Cannot find module 'esbuild'
  Require stack:
  - /Users/**/nodejs-typescript-test/node_modules/serverless-esbuild/dist/index.js
  - /Users/**/nodejs-typescript-test/node_modules/serverless/lib/classes/PluginManager.js
  - /Users/**/nodejs-typescript-test/node_modules/serverless/lib/Serverless.js
  - /Users/**/nodejs-typescript-test/node_modules/serverless/scripts/serverless.js
  - /Users/**/nodejs-typescript-test/node_modules/serverless/bin/serverless.js
      at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
      at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue (/Users/**/nodejs-typescript-test/node_modules/@cspotcode/source-map-support/source-map-support.js:679:30)
      at Function.Module._resolveFilename (/Users/**/nodejs-typescript-test/node_modules/tsconfig-paths/lib/register.js:75:40)
      at Function.Module._load (node:internal/modules/cjs/loader:778:27)
      at Module.require (node:internal/modules/cjs/loader:999:19)
      at require (node:internal/modules/cjs/helpers:102:18)
      at Object.<anonymous> (/Users/**/nodejs-typescript-test/node_modules/serverless-esbuild/dist/index.js:13:19)
      at Module._compile (node:internal/modules/cjs/loader:1097:14)
      at Object.Module._extensions..js (node:internal/modules/cjs/loader:1149:10)
      at Module.load (node:internal/modules/cjs/loader:975:32)
      at Function.Module._load (node:internal/modules/cjs/loader:822:12)
      at Module.require (node:internal/modules/cjs/loader:999:19)
      at require (node:internal/modules/cjs/helpers:102:18)
      at PluginManager.requireServicePlugin (/Users/**/nodejs-typescript-test/node_modules/serverless/lib/classes/PluginManager.js:164:14)
      at /Users/**/nodejs-typescript-test/node_modules/serverless/lib/classes/PluginManager.js:186:27
      at Array.map (<anonymous>)
      at PluginManager.resolveServicePlugins (/Users/**/nodejs-typescript-test/node_modules/serverless/lib/classes/PluginManager.js:183:10)
      at PluginManager.loadAllPlugins (/Users/**/nodejs-typescript-test/node_modules/serverless/lib/classes/PluginManager.js:139:10)
      at Serverless.init (/Users/**/nodejs-typescript-test/node_modules/serverless/lib/Serverless.js:213:30)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
      at async /Users/**/nodejs-typescript-test/node_modules/serverless/scripts/serverless.js:589:7

     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com

  Your Environment Information ---------------------------
     Operating System:          darwin
     Node Version:              17.3.1
     Framework Version:         2.71.0 (local)
     Plugin Version:            5.5.3
     SDK Version:               4.3.0
     Components Version:        3.18.1

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

有人告诉我要添加esbuild,所以我会加上去。

❯ yarn add esbuild
yarn add v1.22.17
[1/5] ?  Validating package.json...
[2/5] ?  Resolving packages...
[3/5] ?  Fetching packages...
[4/5] ?  Linking dependencies...
warning "serverless > @serverless/components > inquirer-autocomplete-prompt@1.4.0" has unmet peer dependency "inquirer@^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0".
[5/5] ?  Building fresh packages...
success Saved lockfile.
success Saved 2 new dependencies.
info Direct dependencies
└─ esbuild@0.14.11
info All dependencies
├─ esbuild-darwin-64@0.14.11
└─ esbuild@0.14.11
✨  Done in 18.79s.

我再次挑战。

❯ yarn sls invoke local -f hello --path src/functions/hello/mock.json
yarn run v1.22.17
$ /Users/**/nodejs-typescript-test/node_modules/.bin/sls invoke local -f hello --path src/functions/hello/mock.json
Serverless: Compiling to node14 bundle with esbuild...
Serverless: Compiling with concurrency: 10
Serverless: Compiling completed.
{
    "statusCode": 200,
    "body": "{\"message\":\"Hello Frederic, welcome to the exciting Serverless world!\",\"event\":{\"headers\":{\"Content-Type\":\"application/json\"},\"body\":{\"name\":\"Frederic\"},\"rawBody\":\"{\\\"name\\\": \\\"Frederic\\\"}\"}}"
}
✨  Done in 6.22s.

看起来收到了正确的回应,所以本地执行似乎很顺利。

部署

讓我們按照 README.md 中的說明,部署到 AWS 上吧。

❯ yarn sls deploy
yarn run v1.22.17
$ /Users/**/nodejs-typescript-test/node_modules/.bin/sls deploy
Serverless: Compiling to node14 bundle with esbuild...
Serverless: Compiling with concurrency: 10
Serverless: Compiling completed.
(node:57802) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead
(Use `node --trace-deprecation ...` to show where the warning was created)
Serverless: Zip function: hello - 13.66 KB [36 ms]
Serverless: Packaging service...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Ensuring that deployment bucket exists
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service hello.zip file to S3 (13.99 kB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................................
Serverless: Stack update finished...
Service Information
service: nodejs-typescript-test
stage: dev
region: us-east-1
stack: nodejs-typescript-test-dev
resources: 13
api keys:
  None
endpoints:
  POST - https://myApiEndpoint/dev/hello
functions:
  hello: nodejs-typescript-test-dev-hello
layers:
  None

Monitor APIs by route with the Serverless Dashboard: run "serverless"
✨  Done in 121.06s.

似乎已完成部署。打开AWS控制台来确认一下吧。

スクリーンショット 2022-01-16 23.27.15.png

我确认Lambda已安全部署,并可通过控制台检查编译和压缩的代码。

スクリーンショット 2022-01-16 23.29.49.png

我们已经部署完成了,现在让我们按照README.md中所描述的方式来确认它的运行行为。

❯ curl --location --request POST 'https://myApiEndpoint/dev/hello' \
      --header 'Content-Type: application/json' \
      --data-raw '{
      "name": "Frederic"
  }'
{"message":"Hello Frederic, welcome to the exciting Serverless world!","event":{"resource":"/hello","path":"/hello","httpMethod":"POST","headers":{"Accept":"*/*","CloudFront-Forwarded-Proto":"https",...

部署完成后,您可以在本地目录中找到一个名为“.serverless”的文件夹。其中包含了CloudFormation的json文件和Lambda函数的zip压缩包。

スクリーンショット 2022-01-17 0.36.25.png

另外,在部署后,您可以检查S3,确认已经创建了类似于nodejs-typescript-test-d-serverlessdeploymentbuck-**的存储桶。

スクリーンショット 2022-01-17 0.41.09.png

当检查内容时,发现有两个文件:compiled-cloudformation-template.json和hello.zip被存储其中。hello.zip文件中是经过编译和压缩的代码。

スクリーンショット 2022-01-16 23.43.18.png

删除项目

删除已部署的服务。

❯ yarn sls remove
yarn run v1.22.17
$ /Users/**/nodejs-typescript-test/node_modules/.bin/sls remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack delete progress...
........................
Serverless: Stack delete finished...

Serverless: Stack delete finished...
✨  Done in 50.42s.

您可以通过这一操作确认 AWS 上的资源已被删除。

总结

我已经使用ServerlessFramework和aws-nodejs-typescript模板检查了一下基本运行情况。虽然说是模板,但已经实现了一些功能,因此需要逐一理解和解读。这与常规模板不同,可能会让人感到困惑,但如果能对此有所帮助就是我的荣幸。

所有的参考链接

Serverless FrameworkをTypeScriptで設定する【serverless.ts】 – Selfs Ryo Com

こちらのブログをとても参考にさせていただきました?ありがとうございます!

Serverless FrameworkでTypeScriptのサーバーレスAPIをAWSにデプロイしてみた – Qiita
Serverless Frameworkの使い方まとめ | Serverless Operations
初めてのサーバーレスアプリケーション開発 ~Serverless Framework を使ってAWSリソースをデプロイする~ | DevelopersIO

广告
将在 10 秒后关闭
bannerAds