# Serverless HTTP

Amazon Web Services is one possible way to host your Node.js application.

This tutorial shows you how to configure Ts.ED application, to be executed as an AWS Lambda Function. Under the hood, @tsed/platform-aws uses serverless-http (opens new window) to handle AWS event and call Express.js/Koa.js application.

TIP

Serverless (opens new window) is a free and open-source web framework written using Node.js. Serverless is the first framework developed for building applications on AWS Lambda, a serverless computing platform provided by Amazon as a part of Amazon Web Services. This tutorial will show you how to install and use Serverless with Ts.ED.

More information here: Official AWS Docs (opens new window)

# Features

This package allows the creation of a Lambda AWS with an Express.js/Koa.js server.

It supports:

  • Mounting Express.js/Koa.js app with serverless-http module,
  • DI injection with @tsed/di,
  • Models mapping using @tsed/schema and @tsed/json-mapper,
  • Params decorators can be used from @tsed/platform-params to get Query, Body, etc...
  • Operation descriptions like @Returns,
  • @tsed/async-hook-context to inject Context anywhere in your class!

# Installation

Generate a new project with the CLI (you can also start from an existing project):

tsed init .
? Choose the target platform: Express.js
? Choose the architecture for your project: Ts.ED
? Choose the convention file styling: Ts.ED
? Check the features needed for your project Swagger, Testing, Linter
? Choose unit framework Jest
? Choose linter tools framework EsLint
? Choose extra linter tools Prettier, Lint on commit
? Choose the package manager: Yarn
1
2
3
4
5
6
7
8
9

This tutorial works also with NPM package manager!

    In the src/lambda create a new Lambda class:

    import {Controller, Inject} from "@tsed/di";
    import {Get, Returns, Summary} from "@tsed/schema";
    import {QueryParams} from "@tsed/platform-params";
    import {TimeslotsService} from "../services/TimeslotsService";
    import {TimeslotModel} from "../models/TimeslotModel";
    
    @Controller("/timeslots")
    export class TimeslotsController {
      @Inject()
      protected timeslotsService: TimeslotsService;
    
      @Get("/")
      @Summary("Return a list of timeslots")
      @Returns(200, Array).Of(TimeslotModel)
      get(@QueryParams("date_start") dateStart: Date, @QueryParams("date_end") dateEnd: Date) {
        return this.timeslotsService.find({
          dateStart,
          dateEnd
        });
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    Remove the http and https port configuration from Server.ts:

    @Configuration({
      // httpPort: 8080,
      // httpsPort: false
    })
    export class Server {}
    
    1
    2
    3
    4
    5

    And add the http port for our local server directly on index.ts file:

    import {PlatformExpress} from "@tsed/platform-express";
    import {Server} from "./Server";
    
    async function bootstrap() {
      const platform = await PlatformExpress.bootstrap(Server, {
        httpsPort: false,
        httpPort: process.env.PORT || 8080,
        disableComponentsScan: true
      });
    
      await platform.listen();
    
      return platform;
    }
    
    bootstrap();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    Create new handler.ts to expose your lambda:

    import {PlatformServerlessHttp} from "@tsed/platform-serverless-http";
    import {PlatformExpress} from "@tsed/platform-express";
    import {Server} from "./Server";
    
    const platform = PlatformServerlessHttp.bootstrap(Server, {
      adapter: PlatformExpress
    });
    
    export const handler = platform.handler();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    Finally, create the serverless.yml:

    service: timeslots
    
    frameworkVersion: "2"
    
    provider:
      name: aws
      runtime: nodejs14.x
      lambdaHashingVersion: "20201221"
    
    plugins:
      - serverless-offline
    
    functions:
      any:
        handler: dist/handler.handler
        events:
          - http:
              method: ANY
              path: /
          - http:
              method: ANY
              path: "{proxy+}"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    # Invoke a lambda with serverless

    Serverless provide a plugin named serverless-offline. This Serverless plugin emulates AWS λ and API Gateway on your local machine to speed up your development cycles. To do so, it starts an HTTP server that handles the request's lifecycle like API does and invokes your handlers.

    So, by using the serverless offline command, we'll be able to invoke our function. For that, we need also to build our code before invoke the lambda.

    To simplify our workflow, we can add the following npm script command in our package.json:

    {
      "scripts": {
        "invoke": "yarn serverless invoke local -f any --data '{\"path\":\"/timeslots\", \"httpMethod\": \"GET\"}'"
      }
    }
    
    1
    2
    3
    4
    5

    Now, we can run the following command to invoke our lambda:

    yarn invoke
    // OR
    npm run invoke
    
    1
    2
    3

    You should see in the terminal the following result:

    {
      "statusCode": 200,
      "body": "[{\"id\":\"b6de4fc7-faaa-4cd7-a144-42f6af0dec6b\",\"title\":\"title\",\"description\":\"description\",\"start_date\":\"2021-10-29T10:40:57.019Z\",\"end_date\":\"2021-10-29T10:40:57.019Z\",\"created_at\":\"2021-10-29T10:40:57.019Z\",\"update_at\":\"2021-10-29T10:40:57.019Z\"}]",
      "headers": {
        "content-type": "application/json",
        "x-request-id": "ebb52d5e-113b-40da-b34e-c14811df596b"
      },
      "isBase64Encoded": false
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # Get Aws Context and Aws Event

    This package includes decorators to easily get the event object Lambda received from API Gateway:

    import {Controller, Get} from "@tsed/common";
    import {ServerlessEvent, ServerlessContext} from "@tsed/platform-serverless-http";
    
    @Controller("/")
    class MyCtrl {
      @Get("/")
      get(@ServerlessEvent() event: any, @ServerlessContext() context: ServerlessContext) {
        console.log("Event", event);
        console.log("Context", context);
    
        return {event, context};
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # Testing

    Ts.ED provide a way to test you lambda with mocked Aws event and context by using the PlatformServerlessTest util.

    Here an example to test a Lambda controller:

    import {PlatformServerless} from "@tsed/platform-serverless-http";
    import {PlatformServerlessTest} from "@tsed/platform-serverless-testing";
    import {PlatformExpress} from "@tsed/platform-express";
    import {Server} from "./Server";
    
    @Controller("/timeslots")
    class TimeslotsController {
      @Get("/")
      getAll() {
        return [];
      }
    }
    
    describe("TimeslotsController", () => {
      beforeEach(
        PlatformServerlessTest.bootstrap(PlatformServerlessHttp, {
          server: Server,
          mount: {
            "/": [TimeslotsLambdaController]
          }
        })
      );
      afterEach(() => PlatformServerlessTest.reset());
    
      it("should call getAll Lambda", async () => {
        const response = await PlatformServerlessTest.request.get("/timeslots");
    
        expect(response.statusCode).toEqual(200);
        expect(JSON.parse(response.body)).toEqual([]);
      });
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    # Author

      # Maintainers Help wanted

        Last Updated: 5/19/2022, 2:53:00 PM

        Other topics