Working with Lambdas
I've been working with AWS Lambdas + Serverless Framework on my projects lately. When I started to work with AWS Lambda I was a bit lost - I was not sure about the best way to develop🛠, debug🐛 and test🧪 AWS Lambdas locally. One thing I knew for sure - AWS web IDE is not the way to go.
This approach uses Serverless Framework specific CLI commands. However, the idea can be generalized and used with other frameworks. I must also mention, that this post will not walk you through the project setup. But you can check the sample repository here.
The Project & Workflow
While working with Lambdas I converged to my current workflow - the topic of this blog.
Project Setup
Consider the following setup for a Lambda project:
-
serverless.yaml
contains the definition of a function(s) - handler path, deployment settings, etc. it's specific to Serverless Framework. More here. -
lambda_1
directory contains an AWS Lambda function. It's possible to have multiple Lambdas per project. You could have something likelambda_2
in your project. You just need to add additional definitions in yourserverless.yaml
file. - I prefer to use
src
directories that hold the source code for a function. -
handler.py
contains the entry point ("lambda handler") for invocation -
event.json
is a sample event for local invocation. More on that soon.
# directory structure
aws_lambda_project
|-lambda_1
| |-src
| |-__init__.py
| |-util.py
| |-db.py
| |-event.json
| |-handler.py
|-serverless.yml
Local Debugging with Serverless Framework
Now, if you want to run/debug your Lambda you can use the Serverless Framework CLI command. Something like this:
sls invoke local --function my_funnction
Or if you have environment variables and events you'd do something like this:
sls invoke local --function my_function --path event.json --env KEY=VALUE
Using these commands is a legit way to run your AWS Lambdas locally. However, in my experience, this invocation is slow(ish) and no debug breakpoints for you.
❗️ Please, let me know if there's a way to execute with Serverless Framework CLI in debug mode with breakpoints.
❗️ Apparently, if you use AWS SAM it works OOB.
Making local debugging more effective & efficient
The workaround and workflow I converged to. I use an additional script, local_handler.py
which wraps handler.py
This allows you to set up your variables, "events", environment variables, and everything you might need. And best of all - you can use breakpoints in your code.
aws_lambda_project
|-lambda_1
| |-src
| |-__init__.py
| |-util.py
| |-db.py
| |-event.json
| |-handler.py
| |-local_handler.py <- THIS
|-serverless.yml
Let's consider the following handler.py
and local_handler.py
.
# handler.py
from lambda_1.src.util import get_db
db = get_db()
def handler(event, context) -> dict:
"Sample lambda function handler."
records = event.get("Records", "")
if records:
db.save(records)
return {"statusCode": 200, "body": "Hello from Lambda!"}
Sidenote: This is the handler you can also invoke with the
sls invoke local
. This simulates an AWS trigger locally. If it works locally, it will likely work on AWS when deployed.
Your local handler should look something like this:
# local_handler.py
from handler import handler
sample_event = {
"Records": [
{"name": "John", "age": "30"},
{"name": "Jane", "age": "25"}
]
}
def main():
print(handler(sample_event, None))
if __name__ == "__main__":
main()
Now in local_handler.py
, I wrap the handler
function with a main
function which is called when you run the local_handler.py
.
This has a couple of advantages IMO:
- you can run your code with Python 🐍 - i.e. breakpoints, IDE capabilities
- you can keep your lambda handler intact and deploy it directly to AWS
- faster startup - no need to initialize Serverless Framework
Un-cluttering deployments
In serverless.yml
it's possible to define files that you do not want to deploy to AWS. The exclamation mark ignores the files. They won't be packaged and pushed - keeping it tidy. See the example below:
...
functions:
sample_lambda:
package:
patterns:
# include
- 'src/**'
# exclude
- '!local_handler.py'
...
- '!venv/**'
handler: lambda_1.handler.handler
environment: ${file(env/.env.sls.json):stage}
...
🏁 Fin. This approach has proven to be the most flexible in my experience with AWS Lambdas.
Feel free to leave a comment or reach out. 📥 💫.
Repository: AWS Sample Repo
Catch me on: github
Catch me on: Twitter
Top comments (0)