Create Your First Lambda with Serverless, Typescript and Microgamma
Tags: serverless, aws, typescript, microservices
Published: 2020-01-16 15:16:50



@microgamma is a framework that I'm developing to make lamdba developing feel like a breeze. With it we can create microservices writing Typescript classes, bind class methods to specific lambda and use the serverless framework for deploying and monitoring without bothering writing much of the configuration needed into `serverless.yml`. @microgamma can also be used without serverless but I think that there is where it shines.

In this first post we'll use `@microgamma/apigator` and `@microgamma/serverless-apigator` serverless plugin. It is the first of a series of posts  where we'll go through the creation of a microservice REST endpoint with DynamoDB and MongoDB including unit and integration tests. 

In this first post we'll focus on
- [create a new Typescript project](#create-a-typescript-project)
- [setup serverless framework and add @microgamma sugar](#setup-serverless-framework-and-microgamma)
- [create a simple lambda](#create-your-first-service)
- [deploy to aws](#deploy-to-aws)
 
You can checkout the code hereby discussed by cloning this [repo]([https://github.com/microgamma/serverless-apigator-blue-print](https://github.com/microgamma/serverless-apigator-blue-print)) at the tag: `my-first-apigator` if you clone the repo you can skip the below section or just follow the readme in there.

--- 

## Create a Typescript project
We'll use `typescript-starter` and I have to admit that it's not optimal for us right now as we'll need to clean some stuff up, though more other things are already done so we can leave with it for now.

`npx typescript-starter my-first-apigator --node --yarn --no-vscode`  

### Clean up
First we want to enable typescript decorators support.
```diff  
--- a/tsconfig.json  
+++ b/tsconfig.json  
@@ -32,8 +32,8 @@  
     "pretty": true /* Stylize errors and messages using color and context. */,  
   
     /* Experimental Options */  
-    // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,  
-    // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,  
+     "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,  
+     "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,  
   
     "lib": ["es2017"],  
     "types": ["node"],  
  
```  
  
then we need to remove `src/lib`, `src/types` and delete the content of `index.ts` and clean `tslint.json` from `Functional style rules`.
```diff  
--- a/tslint.json  
+++ b/tslint.json  
@@ -18,17 +18,9 @@  
     "no-let": true,  
     "no-object-mutation": true,  
     "no-delete": true,  
-    "no-method-signature": true,  
+    "no-method-signature": true  
   
-    // Functional style rules  
-    "no-this": true,  
-    "no-class": true,  
-    "no-mixed-interface": true,  
-    "no-expression-statement": [  
-      true,  
-      { "ignore-prefix": ["console.", "process.exit"] }  
-    ],  
-    "no-if-statement": true  
     /* end tslint-immutable rules */  
+  
   }  
 }  
  
```  
then also
```diff 
--- a/tslint.json
+++ b/tslint.json  
@@ -10,15 +10,13 @@  
     // Recommended built-in rules  
     "no-var-keyword": true,  
     "no-parameter-reassignment": true,  
-    "typedef": [true, "call-signature"],  
   
     // Immutability rules  
     "readonly-keyword": true,  
     "readonly-array": true,  
     "no-let": true,  
     "no-object-mutation": true,  
-    "no-delete": true,  
-    "no-method-signature": true  
+    "no-delete": true  
   
     /* end tslint-immutable rules */  
```  
  
Lastly we want to remove the `build:module` from package.json  and remove `tsconfig.module.json`
```diff  
--- a/package.json  
+++ b/package.json  
@@ -12,7 +12,6 @@  
     "describe": "npm-scripts-info",  
     "build": "run-s clean && run-p build:*",  
     "build:main": "tsc -p tsconfig.json",  
-    "build:module": "tsc -p tsconfig.module.json",  
     "fix": "run-s fix:*",  
     "fix:prettier": "prettier \"src/**/*.ts\" --write",  
     "fix:tslint": "tslint --fix --project .",  
```
and that's all for now. 

---

## Setup Serverless Framework and microgamma

`yarn add -D serverless @microgamma/serverless-apigator`  

`yarn add @microgamma/apigator @microgamma/digator`  


Create serverless.yml file with the following content  
```yaml  
service: my-first-apigator # NOTE: update this with your service name  
  
provider:  
  name: aws  
  runtime: nodejs12.x  
  stage: dev  
  region: eu-west-2  
  
  
plugins:  
  - '@microgamma/serverless-apigator'  
  
custom:  
  apigator:  
    entrypoint: handler  
    buildFolder: build/main  
  
  
```  


## Create your first service  
Create `src/my-first.service.ts` file with the following content  
```typescript  
// src/my-first.service.ts
import { Endpoint } from '@microgamma/apigator'; 
import { Injectable } from '@microgamma/digator';
 

// Define a new endpoint. This will be able to serve all the paths under the root `/` of your endpoint
@Endpoint({  
  // enable cors for this endpoint
  cors: true, 
  // just a reference name
  name: 'my-first-service',
  // this endpoint is public  
  private: false  
})  
@Injectable()
export class MyFirstService {}  
```  
With it we are defining a new endpoint without any lambda (method) yet.

We need to export a bootstrapped version of the service in `src/handler.ts` file such as:
```typescript  
// src/handler.ts
import { bootstrap } from '@microgamma/apigator';  
import { MyFirstService } from './my-first.service';  
  
export = bootstrap(MyFirstService);  
```  
This will be the code that will actually execute in aws. What the bootstrap does is basically to create a new instance of the given service.
  
Now let's check that it builds running `yarn build`. 
A build folder shall be in your path with the following structure :
```  
build/  
└── main  
    ├── handler.d.ts  
    ├── handler.js  
    ├── my-first.service.d.ts  
    └── my-first.service.js  
```  
Now let's create a first, very simple lambda:
```typescript  
// src/my-first.service.ts
import { Endpoint, Lambda } from '@microgamma/apigator';  
import { Injectable } from '@microgamma/digator';
  
@Endpoint({  
  cors: true,  
  name: 'my-first-service',  
  private: false  
})  
@Injectable()
export class MyFirstService {  
  
  @Lambda({  
    method: 'GET',  
    path: '/'  
  })  
  public index() {  
    return `Hello world! Today is ${new Date().toISOString()}`;  
  }  
}  
```  
## Deploy to aws

Build it again `yarn build` and deploy `yarn sls deploy --stage dev` 

Output shall contain the following:
```bash  
endpoints:  
  GET - https://xxxxxx.execute-api.eu-west-2.amazonaws.com/dev/  
```  
  
Which is the url of your lambda. Open your browser and hit that url or `curl` it ;)

You can also use serverless invoke command to invoke it from aws or locally.

  
## Extra points  
Deploy only what's strictly needed excluding everything but the build and node_modules folders.
```diff  
--- a/serverless.yml  
+++ b/serverless.yml  
@@ -1,3 +1,5 @@  
 service: my-first-apigator # NOTE: update this with your service name  
   
 provider:  
@@ -14,3 +16,10 @@ custom:  
   apigator:  
     entrypoint: build/main/handler  
     buildFolder: /home/davide/dev/my-first-apigator/build/main  
+  
+package:  
+  exclude:  
+    - ./*  
+    - ./**  
+    - '!build/**'  
+    - '!node_modules/**  
```  
Add serverless monitoring. Check on serverless.com how to enable monitoring  
```diff  
--- a/serverless.yml  
+++ b/serverless.yml  
@@ -1,3 +1,5 @@  
+org: davidecavaliere  
+app: my-first-apigator  
 service: my-first-apigator # NOTE: update this with your service name  
  
 provider:  
  
```

That's all folks. Hope you enjoyed and thanks for reading. Any feedback is very welcome and stay tuned for more @microgamma sugar.