Understand AWS Lambda functions — The very basics

I bet the first time you encountered this type of architecture you were very confused and looked up to the documentation and thought to yourself: I don’t get what’s going, what’s this whole thing of serverless? when will this get executed? This tutorial is aimed at beginners who are first experiencing Lambda functions.

What’s a lambda function?

Well, in very short words. It’s a piece of code in a container that’s ran whenever it’s invoked. This container is event driven so… what does this mean? It means that any time there’s an event that you told your lambda function to hear it will execute the code inside of it.

Now AWS Lambda functions have different native runtimes, these are:

  • Java

And aside from these ones, you could also provide your own runtimes with some special tweakings. However, that’s not part of the scope of this tutorial. So let’s dig into the part that concerns us.

For this tutorial I’ll be using Python.

How do I create one?

First, let’s take a look at the lambda console and create a new function and in order to understand it we’re going to start off with a blueprint instead of having it done from scratch, so go into your AWS Console and choose the option “Use a blueprint” and in the searchbar search for hello-world-python and select the one that matches the search.

Give it a name and use the default permissions

After you’ve hit the create function button you’ll be taken to the next screen where you will be able to modify your lambda function’s code.

As you can see there’s one function that we already have and it’s called lambda_handler, this is the piece of code that’s ran whenever this function is invoked. And if you observe well, there are two parameters that are being passed to this function: event and context

The event object

The event object (dictionary) it’s the most important one so we’ll leave the context parameter for the end. Do you remember I mentioned that lambda functions are event driven? Well, you can use this to link it to any other component inside AWS and this is what your lambda function take, each event is different and in the case of python this event is a dictionary. In this very basic functioning this event looks like this

{ 
'key1': 'value1',
'key2': 'value2',
'key3': 'value3'
}

So, let’s go line by line. The first line that will get executed is the one from the print statement. Next we have our function and the first line that will get executed it’s the one that says

print('value1 = ' + event['key1'])

This line it’s telling to get the key1 from the event dictionary and retrieve it’s value. The other 2 subsequent lines are doing the same but with keys 2 and 3 respectively.

The last part it’s what your function will return, in this case key1.

return event['key1']

You can return anything you want, it doesn’t have to be the event itself.

We have it there but now what? Let’s test it. In order to test that our function works as intended go ahead and hit the test button, this will pop out a “configure a new test window” Leave the default “hello-world” test, name your test and hit create

Once that’s done, hit test again. If everything went well, you should be seeing something like this.

The response we got is value1, which is what we were expecting from our test event. Every other thing that says function logs correspond to a log in cloudWatch, which our lambda function should have access to, these print statements won’t be returned as a response. But let’s go with a little more complex example.

Customizing the event for a specific resource

So let’s go ahead and create a new event and select the dynamodb-update event. That event looks like this:

{
"Records": [
{
"eventID": "c4ca4238a0b923820dcc509a6f75849b",
"eventName": "INSERT",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"Keys": {
"Id": {
"N": "101"
}
},
"NewImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"ApproximateCreationDateTime": 1428537600,
"SequenceNumber": "4421584500000000017450439091",
"SizeBytes": 26,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899"
},
{
"eventID": "c81e728d9d4c2f636f067f89cc14862c",
"eventName": "MODIFY",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"Keys": {
"Id": {
"N": "101"
}
},
"NewImage": {
"Message": {
"S": "This item has changed"
},
"Id": {
"N": "101"
}
},
"OldImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"ApproximateCreationDateTime": 1428537600,
"SequenceNumber": "4421584500000000017450439092",
"SizeBytes": 59,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899"
},
{
"eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3",
"eventName": "REMOVE",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"Keys": {
"Id": {
"N": "101"
}
},
"OldImage": {
"Message": {
"S": "This item has changed"
},
"Id": {
"N": "101"
}
},
"ApproximateCreationDateTime": 1428537600,
"SequenceNumber": "4421584500000000017450439093",
"SizeBytes": 38,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899"
}
]
}

Let’s say that we need the sequence number and we also need to know in which region was executed. We’ll have to modify our lambda function a little bit. This is what I got:

def lambda_handler(event, context):
response_object = {}
response_object['headers'] = {}
response_object['body'] = {}
sequence_number= event['Records'][2]['dynamodb']['SequenceNumber']
region = event['Records'][2]['awsRegion']

response_object['headers']['statusCode'] = 200
response_object['body']['sequence_number'] = sequence_number
response_object['body']['region'] = region
return response_object

If you test the function the response will look like this:

As you can see, we got the response object that we created with only the two parts that we care about.

The context object

Now, let’s go into the context parameter. This parameter is another object. It provides information about the way in which the lambda function runs. According to AWS it has a bunch of properties, I’ll leave you a link so you can take a further look. For now we’ll only use the aws_request_id. So, by modifiyng a little bit our lambda function we’ll get this code:

def lambda_handler(event, context):
response_object = {}
response_object['headers'] = {}
response_object['body'] = {}
sequence_number= event['Records'][2]['dynamodb']['SequenceNumber']
region = event['Records'][2]['awsRegion']

response_object['headers']['statusCode'] = 200
response_object['body']['sequence_number'] = sequence_number
response_object['body']['region'] = region
requestId = context.aws_request_id
response_object['body']['requestId'] = requestId
return response_object

And if we execute the test we should get this result

And that’s it for this tutorial. I hope I was able to make your transition into writing code to a lambda container in AWS and Serverless architecture a little bit easier.

Software Developer