Day 6 - Lambda - Creating an API



John Rankin @ August 23, 2018

Note:  I've re-deployed to a new region, so I've manually modified the new End points, so the Picture in the URL is different, but its the same output.

For this tutorial we are going to use the serverless application framework for NodeJS.

First lets install the serverless package.

root@damrkul2:~/# npm install -g serverless --ignore-scripts spawn-sync
/usr/bin/serverless -> /usr/lib/node_modules/serverless/bin/serverless
/usr/bin/slss -> /usr/lib/node_modules/serverless/bin/serverless
/usr/bin/sls -> /usr/lib/node_modules/serverless/bin/serverless
+ serverless@1.30.1
+ spawn-sync@2.0.0
added 1 package from 1 contributor and updated 1 package in 8.441s




Once that's been installed, lets create directory, and enter it, and then run the following command. You must specify a template or you will get an error message.

Here is a list of templates we can use for Various languages:

  •  aws-nodejs
  • aws-python
  •  aws-java-maven
  • aws-java-gradle
  • aws-scala-sbt
  • aws-csharp
  • openwhisk-nodejs
  • plugin

For this tutorial we will be using aws-nodejs... but whats cool about this package is we can use another language if needed!

So lets run the command.

root@damrkul2:~/lambda_example# serverless create --template aws-nodejs
Serverless: Generating boilerplate...
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.30.1
-------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name




After executing that command we are left with 2 files in the directory. A handler.js and serverless.yaml file.

serverless.yaml
This is a yaml file that is used to configure the function. For example, we can configure its access privledges, such as assigning IAM roles. This would also be the place where we could define triggers for when the function can be called. For example, we could have this function be triggered when a file is uploaded to the S3 Bucket. The template that is provided comes with alot of commented action examples that you could find useful in the future.

For this tutorial though, we are going to keep it pretty simple.

First, lets rename our serverless application to something useful. Lets modify the value of the to "service: rekous-lambda-test" , which becomes the name of our serverless application.

We will also add an event HTTP event GET request for our function, along with a path for our endpoint.  If this doesnt make sense now, it will in a few minutes.  Anyways,  I've removed all of the comments from the template,  to clean up the file and show you all that is needed for right now.

#
# Rekous First Lambda Function
#

service: rekous-lambda-test
provider:
  name: aws
  runtime: nodejs8.10

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: output/
          method: get

So you can see,  in the functions: , we define out "hello" function.  Inside of the function we specify the actual handler to be used.  And then our events, we are using http , with path and method parameters for that event.  

handler.js

'use strict';

module.exports.hello = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

module.exports.hello shows the name of our function that we specified in the serverless.yaml .  From there this is just a callback function.  The function right now simply prints out a message, and will also print out what exactly is in the event object as well.  I thought  this was personally very nice as it just makes me be able to view what exactly is being thrown into the function at the time of execution.  Anyways, lets just leave the file alone for now..

 

So we have our yaml file setup, along with the basic handler function created for us, lets Deploy .

To Deploy, lets run the following command:

root@damrkul2:~/lambda_example# serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (387 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
................................
Serverless: Stack update finished...
Service Information
service: rekous-lambda-test
stage: dev
region: us-east-1
stack: rekous-lambda-test-dev
api keys:
None
endpoints:
GET - https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/output
functions:
hello: rekous-lambda-test-dev-hello

Well, that was nice, it gave us a bunch of information.  It let us know what values are for our serverless app, such as our service name,  our region that it was deployed at.... and also our endpoints.    

Let's now open up a web browser and go to https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/output

 

Let me fix up this json better for us...

{
	"message": "Go Serverless v1.0! Your function executed successfully!",
	"input": {
		"resource": "/output",
		"path": "/output",
		"httpMethod": "GET",
		"headers": {
			"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
			"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": "z44jotnbnl.execute-api.us-east-1.amazonaws.com",
			"upgrade-insecure-requests": "1",
			"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
			"Via": "2.0 9e51263695f38a00aa0f5c6fd3665d71.cloudfront.net (CloudFront)",
			"X-Amz-Cf-Id": "MmxUpnzpXtRStcXgenHltuBAvi5unV-DntL8dHL5CjuhYngavDHsIQ==",
			"X-Amzn-Trace-Id": "Root=1-5b7ef966-0fe4b5d7febf36c9a1547718",
			"X-Forwarded-For": "136.162.34.1, 52.46.12.75",
			"X-Forwarded-Port": "443",
			"X-Forwarded-Proto": "https"
		},
		"queryStringParameters": null,
		"pathParameters": null,
		"stageVariables": null,
		"requestContext": {
			"resourceId": "w01bsu",
			"resourcePath": "/output",
			"httpMethod": "GET",
			"extendedRequestId": "MFvoBHVeIAMFfwg=",
			"requestTime": "23/Aug/2018:18:13:58 +0000",
			"path": "/dev/output",
			"accountId": "841181805441",
			"protocol": "HTTP/1.1",
			"stage": "dev",
			"requestTimeEpoch": 1535048038561,
			"requestId": "4e0551fa-a700-11e8-bc26-1f336c444547",
			"identity": {
				"cognitoIdentityPoolId": null,
				"accountId": null,
				"cognitoIdentityId": null,
				"caller": null,
				"sourceIp": "136.162.34.1",
				"accessKey": null,
				"cognitoAuthenticationType": null,
				"cognitoAuthenticationProvider": null,
				"userArn": null,
				"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
				"user": null
			},
			"apiId": "z44jotnbnl"
		},
		"body": null,
		"isBase64Encoded": false
	}
}

So you can see when we go to that endpoint, our function is executed, and printed not only the message, but also all of the details of the event object.  

 

Let's think about what this means.....  In a matter of seconds I now have fully functional service up and running.  

The Sky is the Limit! Are you excited about this? I am.

Okay, I've calmed down..... Let me get back to what I want to say...

Another nice feature about lambda is we can easily deploy as many stages as we want.  Recall the previous ENDPOINT: 

https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/output   ... Where we have /dev/ path.  We can easily modify this to lets say "prod" for production.  We simply run  serverless deploy --stage prod  and it deploys with a new endpoint url.


Okay 1 Function, big woop.   Lets add another 1.    Lets create an ECHO function, so we can add a method: post  example.  The echo function will simply read in the POST data, and print it back to the user.     

Lets modify our serverless.yaml

#
# Rekous First Lambda Function
#

service: rekous-lambda-test
provider:
  name: aws
  runtime: nodejs8.10

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: output/
          method: get
  echo:
    handler: handler.echo
    events:
      - http:
          path: echo/
          method: post

Pretty simple so far.  I just copied and pasted the hello function, and did some renaming, and changed the method to post.

handler.js

Lets add our echo function.    We are modeling our function to accept json.    Our payload for our POST will be something like this

{"message":"A Rekous Message"}


Anyways,  Lets add out function to handler.js to reflect that type of data being sent in... and simply return back the POST'ed data.   Here is the new handler.js file

'use strict';

module.exports.hello = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Go Serverless v1.0! Your function executed successfully!',
      input: event,
    }),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};


module.exports.echo = async (event, context) => {
  // Parse the Body that contains our text POST JSON
  var input =  JSON.parse(event.body);

  return {
    statusCode: 200,
    body: JSON.stringify({
      reply: input.message,
    }),
  };
};

That looks about right.....  Lets save it and re-deploy the function.

 

root@damrkul2:~/lambda_example# serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (466 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................
Serverless: Stack update finished...
Service Information
service: rekous-lambda-test
stage: dev
region: us-east-1
stack: rekous-lambda-test-dev
api keys:
  None
endpoints:
  GET - https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/output
  POST - https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/echo
functions:
  hello: rekous-lambda-test-dev-hello
  echo: rekous-lambda-test-dev-echo
Serverless: Removing old service artifacts from S3...

 

You can see now that our new function endpoint has been created.... And if you tried to open that in your webbrowser by itself, you will get an Missing Authentication Token because you tried to do a GET on a POST function...  

So lets send a POST request to test it out ... using cURL from the command line...

root@damrkul2:~/lambda_example# curl -d '{"message":"Rekous Message"}' -X POST https://wjvddbj9h1.execute-api.us-west-2.amazonaws.com/dev/echo             
{"reply":"Rekous Message"}

 

So you can see here that we our function replied with the data we sent....   Just using these functions  we can build out a serverless API very easily.   

Lambda seems pretty easy to use, and I already have some fun ideas I could build using lambda functions.    Do you?

 

 

 

 

 

 

 

Most Recent Posts


Blog Test

Read This
October 16, 2018

Day 7 - DynamoDB - and Working with 2 Services - Lambda

Read This
August 25, 2018

Day 6 - Lambda - Creating an API

Read This
August 23, 2018

AWS - Day 5 - S3 - Simple Storage Service

Read This
August 22, 2018

AWS - Day 4 - AWS CLI Useful Scripts

Read This
August 21, 2018

AWS - Day 3 - Create Container from another container

Read This
August 20, 2018

Day 2 - Docker Intro

Read This
August 17, 2018

AWS - Day1 - Tutorial - Launching my first Docker Container

Read This
August 16, 2018

AWS - Day 1 - Signing up and testing out their tutorials

Read This
August 16, 2018

Dynamic Programming - Edit Distance

Read This
December 19, 2016

Dynamic Programming - Fibonacci

Read This
December 19, 2016

What I Do

Read This
June 23, 2015

First Blog Post

Read This
June 23, 2015