- Server Application Setup
- Tools
- Create Applications
- Nest Application
- Build, Test, Serve Application
- Going Serverless
- API Key and Secret Key
- Lambda Function Entry Point
- Serverless Configuration (serverless.yml)
- Deploy
- Results
- Serverless with NestJS + Lambda Functions
- Nest Domain Library
- NestJS Domain Library
- Swagger Documentation
- Generate API Documentation
- Postman
- Resources
Install Nx Nest developer tools.
More information at: https://nx.dev/angular/plugins/nest/overview. Make sure to use a version compatible with the Angular version you are targeting. For example, the Angular workspace is using Angular 9.x.x, therefore use a compatible version. See: https://www.npmjs.com/package/@nrwl/nest for a list of package versions.
# npm install -D @nrwl/nest
yarn add @nrwl/nest@10.4.7
yarn global add @nrwl/[email protected]
Create a new server application using Nx. In our example below we are creating a contacts
server application to manage contacts for the client SPA. By default it creates the server application in the apps
folder - which makes sense. It is an application, it is deployable. I wanted to put the project in a server
folder, however, the server is just where the application is hosted.
Use the following command to create the server-side
contacts
application. The default location is in the apps folder - to make server applications more distinct the location will beapps/server/my-server-app
.nx generate @nrwl/nest:application optimizr --directory=server --frontend-project=creatr --dry-run
The CLI output is as follows:
nx generate @nrwl/nest:application optimizr --directory=server --frontend-project=creatr
CREATE apps/server/optimizr/.eslintrc.json (83 bytes)
CREATE apps/server/optimizr/tsconfig.app.json (240 bytes)
CREATE apps/server/optimizr/tsconfig.json (200 bytes)
CREATE apps/server/optimizr/src/main.ts (553 bytes)
CREATE apps/server/optimizr/src/app/.gitkeep (0 bytes)
CREATE apps/server/optimizr/src/assets/.gitkeep (0 bytes)
CREATE apps/server/optimizr/src/environments/environment.prod.ts (52 bytes)
CREATE apps/server/optimizr/src/environments/environment.ts (53 bytes)
CREATE apps/server/optimizr/jest.config.js (343 bytes)
CREATE apps/server/optimizr/tsconfig.spec.json (202 bytes)
CREATE apps/creatr/proxy.conf.json (79 bytes)
CREATE apps/server/optimizr/src/app/app.controller.spec.ts (627 bytes)
CREATE apps/server/optimizr/src/app/app.controller.ts (265 bytes)
CREATE apps/server/optimizr/src/app/app.module.ts (250 bytes)
CREATE apps/server/optimizr/src/app/app.service.spec.ts (505 bytes)
CREATE apps/server/optimizr/src/app/app.service.ts (175 bytes)
UPDATE package.json (4490 bytes)
UPDATE angular.json (37082 bytes)
UPDATE nx.json (1924 bytes)
UPDATE jest.config.js (1049 bytes)
√ Packages installed successfully.
The Workspace is updated with the following packages. An application project is added to the angular.json file.
"@nrwl/nest": "10.4.7",
"@nrwl/node": "10.4.7",
{
"/api": {
"target": "http://localhost:3333",
"secure": false
}
}
The main.ts
is the entry point and loader of the Nest application. It will bootstrap the application using the NestFactory.create()
method.
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import { Logger } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app/app.module";
async function bootstrap() {
// use to create a Nest application instance; [create(..)] returns an application instance
const app = await NestFactory.create(AppModule);
const globalPrefix = "api";
app.setGlobalPrefix(globalPrefix);
const port = process.env.PORT || 3333;
await app.listen(port, () => {
Logger.log("Listening at http://localhost:" + port + "/" + globalPrefix);
});
}
bootstrap();
The create(..)
method returns an application object fulfilling the following interface INestApplication
.
import { CorsOptions } from "./external/cors-options.interface";
import { CanActivate } from "./features/can-activate.interface";
import { NestInterceptor } from "./features/nest-interceptor.interface";
import { HttpServer } from "./http/http-server.interface";
import {
ExceptionFilter,
INestMicroservice,
NestHybridApplicationOptions,
PipeTransform,
} from "./index";
import { INestApplicationContext } from "./nest-application-context.interface";
import { WebSocketAdapter } from "./websockets/web-socket-adapter.interface";
/**
* Interface defining the core NestApplication object.
*
* @publicApi
*/
export interface INestApplication extends INestApplicationContext {
/**
* A wrapper function around HTTP adapter method: `adapter.use()`.
* Example `app.use(cors())`
*
* @returns {void}
*/
use(...args: any[]): this;
/**
* Enables CORS (Cross-Origin Resource Sharing)
*
* @returns {void}
*/
enableCors(options?: CorsOptions): void;
/**
* Starts the application.
*
* @param {number} port
* @param {string} hostname
* @param {Function} callback Optional callback
* @returns A Promise that, when resolved, is a reference to the underlying HttpServer.
*/
listen(port: number | string, callback?: () => void): Promise<any>;
listen(
port: number | string,
hostname: string,
callback?: () => void
): Promise<any>;
/**
* Returns the url the application is listening at, based on OS and IP version. Returns as an IP value either in IPv6 or IPv4
*
* @returns The IP where the server is listening
*/
getUrl(): Promise<string>;
/**
* Starts the application (can be awaited).
*
* @param {number} port
* @param {string} hostname (optional)
* @returns {Promise}
*/
listenAsync(port: number | string, hostname?: string): Promise<any>;
/**
* Registers a prefix for every HTTP route path.
*
* @param {string} prefix The prefix for every HTTP route path (for example `/v1/api`)
* @returns {void}
*/
setGlobalPrefix(prefix: string): this;
/**
* Setup Ws Adapter which will be used inside Gateways.
* Use when you want to override default `socket.io` library.
*
* @param {WebSocketAdapter} adapter
* @returns {void}
*/
useWebSocketAdapter(adapter: WebSocketAdapter): this;
/**
* Connects microservice to the NestApplication instance. Transforms application
* to a hybrid instance.
*
* @param {T} options Microservice options object
* @param {NestHybridApplicationOptions} hybridOptions Hybrid options object
* @returns {INestMicroservice}
*/
connectMicroservice<T extends object = any>(
options: T,
hybridOptions?: NestHybridApplicationOptions
): INestMicroservice;
/**
* Returns array of the microservices connected to the NestApplication.
*
* @returns {INestMicroservice[]}
*/
getMicroservices(): INestMicroservice[];
/**
* Returns the underlying native HTTP server.
*
* @returns {any}
*/
getHttpServer(): any;
/**
* Returns the underlying HTTP adapter.
*
* @returns {HttpServer}
*/
getHttpAdapter(): HttpServer;
/**
* Starts all connected microservices asynchronously.
*
* @param {Function} callback Optional callback function
* @returns {void}
*/
startAllMicroservices(callback?: () => void): this;
/**
* Starts all connected microservices and can be awaited.
*
* @returns {Promise}
*/
startAllMicroservicesAsync(): Promise<void>;
/**
* Registers exception filters as global filters (will be used within
* every HTTP route handler)
*
* @param {ExceptionFilter[]} ...filters
*/
useGlobalFilters(...filters: ExceptionFilter[]): this;
/**
* Registers pipes as global pipes (will be used within every HTTP route handler)
*
* @param {PipeTransform[]} ...pipes
*/
useGlobalPipes(...pipes: PipeTransform<any>[]): this;
/**
* Registers interceptors as global interceptors (will be used within
* every HTTP route handler)
*
* @param {NestInterceptor[]} ...interceptors
*/
useGlobalInterceptors(...interceptors: NestInterceptor[]): this;
/**
* Registers guards as global guards (will be used within every HTTP route handler)
*
* @param {CanActivate[]} ...guards
*/
useGlobalGuards(...guards: CanActivate[]): this;
/**
* Terminates the application (including NestApplication, Gateways, and each connected
* microservice)
*
* @returns {Promise<void>}
*/
close(): Promise<void>;
}
Build the application with configuration options (optional). By default, the serve command will run in watch mode. This allows code to be changed, and the Nest application to be rebuilt automatically. Nest applications also have the inspect flag set, so you can attach your debugger to the running instance.
nx build <nest-app> --configuration=production
Serve the application.
nx serve <nest-app> --port=<1234>
The output of the serve command.
ng serve contacts
Starting type checking service...
Using 6 workers with 2048MB memory limit
Type checking in progress...
Hash: 1f50371ef6fa884e85fd
Built at: 09/05/2020 1:04:14 PM
Entrypoint main = main.js main.js.map
chunk {main} main.js, main.js.map (main) 2.26 KiB [entry] [rendered]
Debugger listening on ws://localhost:52440/60c03caa-d750-4526-9040-d8e89f03d08b
Debugger listening on ws://localhost:52441/60c03caa-d750-4526-9040-d8e89f03d08b
For help, see: https://nodejs.org/en/docs/inspector
[Nest] 12364 - 09/05/2020, 1:04:20 PM [NestFactory] Starting Nest application...
[Nest] 12364 - 09/05/2020, 1:04:20 PM [InstanceLoader] AppModule dependencies initialized +45ms
[Nest] 12364 - 09/05/2020, 1:04:20 PM [RoutesResolver] AppController {/api}: +119ms
[Nest] 12364 - 09/05/2020, 1:04:20 PM [RouterExplorer] Mapped {/api, GET} route +20ms
[Nest] 12364 - 09/05/2020, 1:04:20 PM [NestApplication] Nest application successfully started +8ms
[Nest] 12364 - 09/05/2020, 1:04:21 PM Listening at http://localhost:3333/api +478ms
No type errors found
Version: typescript 3.7.5
Time: 9415ms
Load the URL in the browser: http://localhost:3333/api
The api output is:
{ "message": "Welcome to contacts!" }
The HTTP API request uses the Express
server to host and process requests.
Request URL: http://localhost:3333/api
Request Method: GET
Status Code: 304 Not Modified
Remote Address: [::1]:3333
Referrer Policy: no-referrer-when-downgrade
Content-Length: 31
Content-Type: application/json; charset=utf-8
Date: Sat, 15 Aug 2020 23:53:01 GMT
ETag: W/"1f-q36TEEewAbYKCWpxEMRYeqr977U"
X-Powered-By: Express
Other application commands.
nx lint <nest-app>
nx test <nest-app>
See https://serverless.com for more information about targeting multiple platforms (i.e., Google Cloud Platform, AWS, or Azure) with a single YAML configuration.
- requires AWS Account
- use Serverless package
- https://github.com/serverless/serverless
Install serverless package.
yarn global add serverless
Use to create the serverless.yml
configuration for a target application.
sls create --template aws-nodejs --name <YOU-APP-NAME-HERE>
Create a lambda.ts
file in the src folder of the target application. The example is an application that we want to host on AWS Lambda called video
. The serverless
build process will use this file to build the application.
ex: apps\video\src\lambda.ts
Pre-requisites: You will need to install some additional packages. Since we are using TypeScript, install the type definition packages also.
yarn add --dev @types/aws-lambda
yarn add --dev @types/aws-serverless-express
yarn add --dev aws-lambda
yarn add --dev aws-serverless-express
yarn add --dev serverless-offline
yarn add --dev serverless-plugin-optimize
yarn add --dev serverless-plugin-typescript
Create a new IAM user account in AWS. Copy or download the keys. Run the serverless
command to create a credential store/file. On Windows, the credential file is stored in the .aws/credential
location in your local users/<NAME>
folder.
Short video on creating a new IAM user and accessing keys. https://www.youtube.com/watch?v=KngM5bfpttA
Your development environment requires AWS credentials. Use the serverless
CLI to create the file with the API and secret keys. The file supports multiple profilea. Therefore, you can have different IAM user accounts associated to different profiles.
The CLI has a --help
to provide the information for a credentials file.
serverless config credentials --help
Plugin: AwsConfigCredentials
config credentials ............ Configures a new provider profile for the Serverless Framework
--provider / -p (required) ......... Name of the provider. Supported providers: "aws"
--key / -k (required) .............. Access key for the provider
--secret / -s (required) ........... Secret key for the provider
--profile / -n ..................... Name of the profile you wish to create. Defaults to "default"
--overwrite / -o ................... Overwrite the existing profile configuration in the credentials file
Use the serverless CLI command to create the credentials file using the api and secret key values from the AWS IAM account.
serverless config credentials --provider aws --key <api-key> --secret <secret-key>
serverless config credentials --provider aws --profile <default|custom-name> --key <api-key> --secret <secret-key>
Use the serverless deploy
command to use the default
AWS profile. If you only have 1, then it is the default. An environment with multiple profiles will have a credential file that looks like the following sample (not real values).
[default]
aws_access_key_id=AKIAW7K
aws_secret_access_key=sX/flzzjJHEMPLJG1/jKCAD
[quicken]
aws_access_key_id=AKIAJVWCJ
aws_secret_access_key=cxENQdXHpF/z6Xq9JYxW
To target a specific profile, use the serverless deploy
command with the option --aws-profile
.
serverless deploy --aws-profile quicken
Typically, the entry point to a NextJS node application is the main.ts
which uses a factory to create the application and hosting with Express. However, the purpose of the lambda.ts
file is to provide an entry point for the server application on AWS Lambda Function. It loads the server application's AppModule
and available Controllers.
Create a
lambda.ts
file in thesrc
folder of the server application. Configure it to use and load the application'sAppModule
import { Handler, Context } from "aws-lambda";
import { Server } from "http";
import { createServer, proxy } from "aws-serverless-express";
import { eventContext } from "aws-serverless-express/middleware";
import { NestFactory } from "@nestjs/core";
import { ExpressAdapter } from "@nestjs/platform-express";
import { AppModule } from "./app/app.module";
const express = require("express");
const binaryMimeTypes: string[] = [];
let cachedServer: Server;
async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp)
);
nestApp.use(eventContext());
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}
// Export the handler : the entry point of the Lambda function
export const main: Handler = async (event: any, context: Context) => {
cachedServer = await bootstrapServer();
return proxy(cachedServer, event, context, "PROMISE").promise;
};
The function name
identifies the function and the map to the specified:
- handler
- event(s)
The handler
is invoked when a specified event
is triggered. In this case, the Lambda function is configured to run when an HTTP trigger (e.g., AWS Gateway API) of http://<AWS-PATH-TO-FUNCTION>/hello
is invoked by a client using the HTTP method get
.
The plugins
configuration allows the build and deploy process to use the plugins. The serverless-plugin-typescript
plugin is required if you are using NestJS with TypeScript.
Note The .zip output file size is limited to 250MB More information.
service: contacts
# app and org for use with dashboard.serverless.com
org: angulararchitecture
app: quicken-contacts
plugins:
- serverless-plugin-typescript
- serverless-plugin-optimize
# - serverless-offline
# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"
provider:
name: aws
runtime: nodejs12.x
region: us-west-1
stage: ${opt:stage, 'dev'}
functions:
api:
handler: ./apps/contacts/src/lambda.main
events:
- http:
cors: true
method: any
path: /{any+}
- http:
cors: true
method: any
path: /
The application name must exist on your https://app.serverless.com/. The name must match the configuration of the app
name in the serverless.yml
file.
app: quicken-contacts
Error: {"errorMessage":"Application not found. - Please contact support and provide this identifier to reference this issue - 4WDV3L4LDWCS"}
Running the serverless deploy --aws-profile quicken-contacts
command produces the following output.
serverless deploy --aws-profile quicken
Serverless: Compiling with Typescript...
Serverless: Using local tsconfig.json
Serverless: Typescript compiled.
Serverless: Optimize: starting engines
Serverless: Optimize: contacts-dev-api
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Installing dependencies for custom CloudFormation resources...
Serverless: Deprecation warning: Safeguards support has been moved to the @serverless/safeguards-plugin external plugin and will be removed from the core with next major release.
Please visit https://github.com/serverless/safeguards-plugin/ to migrate your safeguards to the new plugin.
You may also disable safeguards by setting "custom.safeguards.isDisabled: true" in service config
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service contacts.zip file to S3 (997.73 KB)...
Serverless: Uploading custom CloudFormation resources...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
............................................................
Serverless: Stack update finished...
Service Information
service: contacts
stage: dev
region: us-west-1
stack: contacts-dev
resources: 21
api keys:
Serverless: Stack update finished...
Service Information
service: contacts
stage: dev
region: us-west-1
stack: contacts-dev
resources: 21
api keys:
None
endpoints:
ANY - https://juz537ocx2.execute-api.us-west-1.amazonaws.com/dev/{any+}
ANY - https://juz537ocx2.execute-api.us-west-1.amazonaws.com/dev/
functions:
api: contacts-dev-api
layers:
None
Serverless: Publishing service to the Serverless Dashboard...
Serverless: Successfully published your service to the Serverless Dashboard: https://dashboard.serverless.com/tenants/angulararchitecture/applications/quicken-contacts/services/contacts/stage/dev/region/us-west-1
Use the URL to view the results in your browser. The event
information from the HTTP request is included in the response.
GET - https://XXXXXXX.execute-api.us-west-1.amazonaws.com/dev/hello
The results of the HTTP get request:
{
"message": "Hello Serverless Videos...",
"input": {
"resource": "/hello",
"path": "/hello",
"httpMethod": "GET",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "pt5dwy8gif.execute-api.us-west-1.amazonaws.com",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"Via": "2.0 29f1da35ce271d2cdc88184ed0c1f86d.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "D3wi7uA1FkSzKyVQuUR5AkNy9ThH32KjSTBIZJZvhQ4cyqu0HgvwmQ==",
"X-Amzn-Trace-Id": "Root=1-5f42d145-a5fa292e8052fda0df091691",
"X-Forwarded-For": "75.166.173.243, 70.132.0.173",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
],
"Accept-Encoding": ["gzip, deflate, br"],
"Accept-Language": ["en-US,en;q=0.9"],
"CloudFront-Forwarded-Proto": ["https"],
"CloudFront-Is-Desktop-Viewer": ["true"],
"CloudFront-Is-Mobile-Viewer": ["false"],
"CloudFront-Is-SmartTV-Viewer": ["false"],
"CloudFront-Is-Tablet-Viewer": ["false"],
"CloudFront-Viewer-Country": ["US"],
"Host": ["pt5dwy8gif.execute-api.us-west-1.amazonaws.com"],
"sec-fetch-dest": ["document"],
"sec-fetch-mode": ["navigate"],
"sec-fetch-site": ["none"],
"sec-fetch-user": ["?1"],
"upgrade-insecure-requests": ["1"],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"
],
"Via": [
"2.0 29f1da35ce271d2cdc88184ed0c1f86d.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id": [
"D3wi7uA1FkSzKyVQuUR5AkNy9ThH32KjSTBIZJZvhQ4cyqu0HgvwmQ=="
],
"X-Amzn-Trace-Id": ["Root=1-5f42d145-a5fa292e8052fda0df091691"],
"X-Forwarded-For": ["75.166.173.243, 70.132.0.173"],
"X-Forwarded-Port": ["443"],
"X-Forwarded-Proto": ["https"]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "9nhg8a",
"resourcePath": "/hello",
"httpMethod": "GET",
"extendedRequestId": "RvWi0HkMyK4FWFg=",
"requestTime": "23/Aug/2020:20:27:49 +0000",
"path": "/dev/hello",
"accountId": "516631029472",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "pt5dwy8gif",
"requestTimeEpoch": 1598214469144,
"requestId": "6601c39f-f0d6-460d-a08d-8fa10cd69750",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"sourceIp": "75.166.173.243",
"principalOrgId": null,
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
"user": null
},
"domainName": "XXXXXXX.execute-api.us-west-1.amazonaws.com",
"apiId": "pt5dwy8gif"
},
"body": null,
"isBase64Encoded": false
}
}
Update the serverless.yml
to map the HTTP event
paths to any
and a default without a path. The /
allows a request without any path information.
service: video-serverless-test
# app and org for use with dashboard.serverless.com
org: angulararchitecture
app: video-serverless
plugins:
- serverless-plugin-typescript
- serverless-plugin-optimize
# - serverless-offline
provider:
name: aws
runtime: nodejs12.x
region: us-west-1
stage: ${opt:stage, 'dev'}
functions:
api:
handler: ./apps/video/src/lambda.main
# 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:
- http:
cors: true
method: any
path: /{any+}
- http:
cors: true
method: any
path: /
The lambda.ts
file provides a handler function that returns a NestJS
application context/proxy.
import { Handler, Context } from "aws-lambda";
import { Server } from "http";
import { createServer, proxy } from "aws-serverless-express";
import { eventContext } from "aws-serverless-express/middleware";
import { NestFactory } from "@nestjs/core";
import { ExpressAdapter } from "@nestjs/platform-express";
import { AppModule } from "./app/app.module";
const express = require("express");
const binaryMimeTypes: string[] = [];
let cachedServer: Server;
// Create the Nest.js server and convert it into an Express.js server
async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp)
);
nestApp.use(eventContext());
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}
// Export the handler : the entry point of the Lambda function
export const main: Handler = async (event: any, context: Context) => {
cachedServer = await bootstrapServer();
return proxy(cachedServer, event, context, "PROMISE").promise;
};
The NestJS application provides a controller with with (2) API endpoints:
import { Controller, Get } from "@nestjs/common";
import { AppService } from "./app.service";
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getData() {
return this.appService.getData();
}
@Get("hello")
sayHello() {
return `Hello Serverless from NestJS + Express application!`;
}
}
Create a server-side library for the NestJs application. Use the Nx command:
nx generate @nrwl/nest:library api/contacts --service --global --buildable
The output of the CLI command is:
nx generate @nrwl/nest:library api/contacts --service --global --buildable
CREATE libs/api/contacts/tslint.json (94 bytes)
CREATE libs/api/contacts/README.md (178 bytes)
CREATE libs/api/contacts/tsconfig.json (147 bytes)
CREATE libs/api/contacts/src/index.ts (87 bytes)
CREATE libs/api/contacts/jest.config.js (283 bytes)
CREATE libs/api/contacts/tsconfig.spec.json (252 bytes)
CREATE libs/api/contacts/package.json (61 bytes)
CREATE libs/api/contacts/src/lib/api-contacts.module.ts (255 bytes)
CREATE libs/api/contacts/src/lib/api-contacts.service.spec.ts (445 bytes)
CREATE libs/api/contacts/src/lib/api-contacts.service.ts (115 bytes)
UPDATE tsconfig.json (1171 bytes)
UPDATE angular.json (19018 bytes)
UPDATE nx.json (908 bytes)
The angular.json is updated with the new library project for the API.
"api-contacts": {
"root": "libs/api/contacts",
"sourceRoot": "libs/api/contacts/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/api/contacts/tsconfig.lib.json", "libs/api/contacts/tsconfig.spec.json"],
"exclude": ["**/node_modules/**", "!libs/api/contacts/**/*"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/api/contacts/jest.config.js",
"tsConfig": "libs/api/contacts/tsconfig.spec.json",
"passWithNoTests": true
}
},
"build": {
"builder": "@nrwl/node:package",
"options": {
"outputPath": "dist/libs/api/contacts",
"tsConfig": "libs/api/contacts/tsconfig.lib.json",
"packageJson": "libs/api/contacts/package.json",
"main": "libs/api/contacts/src/index.ts",
"assets": ["libs/api/contacts/*.md"]
}
}
}
}
The tsconfig.json paths
contains a new entry for the library project. It is using the @valencia
npm scope name.
"@valencia/api/contacts": ["libs/api/contacts/src/index.ts"]
nx g @nrwl/nest:library optimizr --directory=api/optimizr --import-path=@valencia/api/optimizr --service
CREATE libs/api/optimizr/optimizr/.eslintrc.json (86 bytes)
CREATE libs/api/optimizr/optimizr/README.md (196 bytes)
CREATE libs/api/optimizr/optimizr/tsconfig.json (203 bytes)
CREATE libs/api/optimizr/optimizr/tsconfig.lib.json (256 bytes)
CREATE libs/api/optimizr/optimizr/src/index.ts (105 bytes)
CREATE libs/api/optimizr/optimizr/jest.config.js (396 bytes)
CREATE libs/api/optimizr/optimizr/tsconfig.spec.json (255 bytes)
CREATE libs/api/optimizr/optimizr/src/lib/api-optimizr-optimizr.module.ts (278 bytes)
CREATE libs/api/optimizr/optimizr/src/lib/api-optimizr-optimizr.service.spec.ts (494 bytes)
CREATE libs/api/optimizr/optimizr/src/lib/api-optimizr-optimizr.service.ts (103 bytes)
UPDATE tsconfig.base.json (2233 bytes)
UPDATE angular.json (37251 bytes)
UPDATE nx.json (1983 bytes)
UPDATE jest.config.js (1133 bytes)
Use the following package to add decorators to the controller API endpoint methods. Learn more about the Swagger configuration at https://medium.com/javascript-in-plain-english/serverless-nestjs-document-your-api-with-swagger-and-aws-api-gateway-64a53962e8a2
yarn add @nestjs/swagger -S
Update the NestJS application loader to generate Swagger documentation using the @nestjs/swagger
tools. Update the controller methods with decorators to include the details about the specified API and response.
https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/
Install the required package. It is used during the documentation build routines for Swagger.
swagger-ui-dist
- deploy the application using
serverless deploy --aws-profile quicken
- Go to the endpoint and add
/api
to view the Swagger documentation - add the
-json
to view the Swagger documentation in JSON format. - load and view the JSON at https://editor.swagger.io
- download the
.yml
file that contains your Swagger documentation. - Use the AWS Console and import the
.yml
file in the Amazon API Gateway. - Share the documentation via
- Swagger
- API Gateway
- Postman
{
"isSuccess": false,
"message": "Error while attempting to retrieve contact. Error: {\"response\":{\"message\":\"User: arn:aws:sts::516631029472:assumed-role/contacts-dev-us-west-1-lambdaRole/contacts-dev-api is not authorized to perform: dynamodb:GetItem on resource: arn:aws:dynamodb:us-west-1:516631029472:table/ContactsTable-dev\",\"code\":\"AccessDeniedException\",\"time\":\"2020-09-10T01:57:30.830Z\",\"requestId\":\"8J32LVTNKKIPSVPAN836POQ483VV4KQNSO5AEMVJF66Q9ASUAAJG\",\"statusCode\":400,\"retryable\":false,\"retryDelay\":27.598537698082804},\"status\":500,\"message\":\"User: arn:aws:sts::516631029472:assumed-role/contacts-dev-us-west-1-lambdaRole/contacts-dev-api is not authorized to perform: dynamodb:GetItem on resource: arn:aws:dynamodb:us-west-1:516631029472:table/ContactsTable-dev\"}",
"messages": [
{
"code": "CONTACT_ERROR",
"message": "Unexpected error while attempting to retrieve contact",
"messageType": "Error"
}
]
}
Just stumbled across this and it's been super helpful, thanks! Could you elaborate on the 'Proxy Configuration' section? I'm curious what that is, and where it's supposed to go...