4 minutes read

POSTED Feb, 2019 dot IN Serverless

Introduction to AWS Chalice: Making Python Lambda Development Easier

Mustafa Motani

Written by Mustafa Motani


Ex-Software Engineer @Thundra

linkedin-share
introduction-to-aws-chalice X

Editor’s Note: This post was originally published in February 2019 and has been updated in September 2020

Serverless is still an unexplored realm for many developers, and the idea of transitioning from traditional servers to serverless may look like a daunting task. Fortunately, the serverless ecosystem has produced several powerful frameworks and abstractions to quickly and easily deploy serverless infrastructure.

In this blog, I’ll be introducing one such microframework for Python, developed by engineers at AWS, called Chalice.

Getting Started

A common stumbling block for developers is having to manage the configuration and deployment of cloud resources like serverless functions. AWS provides Chalice as an installable Python package via pip, allowing a developer to have a deployed serverless application in a matter of minutes.

With Chalice, most of the abovementioned management burden is handled behind the scenes, so developers can focus on the code. Moreover, Chalice shares a lot of similarity with the Flask web microframework in terms of code structure, making it easier for developers who are familiar with Flask to adopt Python serverless development.

Prerequisites

The Chalice GitHub page provides a quickstart tutorial for getting a Chalice project started, but it assumes you already have the following installed and set up:

  • python 3 (>~3.7)
  • Virtualenv
  • An AWS account with API/CLI access and local credentials

There are a couple of additional tools worth considering if you are pursuing Python development:

  • virtualenvwrapper makes managing separate virtual environments much easier.
  • pyenv is like a virtualenv for Python binaries, allowing you to seamlessly switch between separate Python versions.

Chalice Sample Project

If you just want to follow the AWS-provided quickstart instructions, use the following code:


$ python3 --version
 Python 3.7.3 
 $ python3 -m venv venv37
 $ . venv37/bin/activate
 

If you have virtualenvwrapper installed, it’s a lot simpler:


$ mkvirtualenv chalice
 $ pip install chalice 
 

After installing Chalice with either method, verify that it’s working:


$ chalice --help
 Usage: chalice [OPTIONS] COMMAND [ARGS]...
 Options:
 ...
 

Now that Chalice is installed, we can simply create an app using the ”new-project” command. Call the app “hello-world”:


$ chalice new-project hello-world


This creates a new directory with a bare minimum of files necessary to deploy and get your app up and running. Next, navigate to your project directory, and deploy your project to AWS:


$ cd hello-world
 $ chalice deploy
 

You should see an IAM role, Lambda function, and API Gateway deployed to your AWS account:


Creating deployment package.
 Creating IAM role: hello-world
 Creating lambda function: hello-world
 Creating Rest API
 Resources deployed:
 -Lambda ARN: 
arn:aws:lambda:eu-west-2:111111111111:function:project-name -Rest API URL: https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/api/

NOTE: I have obfuscated my API URL, as well as my AWS account. You will see unique values for your own output.

Now, try making a request to the API:


$ curl -X GET 
 https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/api/
 

You should see the following response: 


{"hello":"world"}

In the project directory, you should see a file called “app.py,” which is the basic Python module that defines how the app behaves. The contents of the file should be familiar to anyone who has previously worked with Flask:


from chalice import Chalice 
 app = Chalice(app_name='hello-world')
 @app.route('/') 
 def index(): 
     return {'hello': 'world'}

Congratulations! You’ve successfully set up and run a Chalice “Hello World” app. You can see how simple it was to set up and deploy your Lambda function, as Chalice handles setting up the required IAM role and policy and gives you a functional API Gateway right away.

In the next section, we’ll try out some more interesting examples with Chalice.

Adding Functionality

In the “hello-world” app, a single “index” function was configured to return ‘hello’: ‘world’ whenever an empty GET request was made against the API endpoint. Let’s now add a little more functionality to our app with a “FizzBuzz” endpoint.

For those not familiar with FizzBuzz, it’s a popular coding exercise often given in programming interviews. This HackerRank exercise is a classic example. For the Chalice app, you’ll be adding a “fizzbuzz” endpoint that returns “Fizz”, “Buzz” or “FizzBuzz” depending on the number passed in the request. If the number is not divisible by 3 or 5, it will return the number instead.First, add the following app route to the “app.py” file:

@app.route('/fizzbuzz/{number}')   
def fizzbuzz(number): number = int(number) if number % 3 == 0 and number % 5 == 0: answer = "FizzBuzz elif number % 5 == 0: answer = "Buzz" elif number % 3 == 0: answer = "Fizz" else: answer = number return {'answer': answer}

Now, deploy the project again with chalice deploy and make the following request:

  $ curl -X GET 
  https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/fizzbuzz/15

This will call the `fizzbuzz()` function instead and return:

 {"answer":"fizzbuzz"}

Try some different numbers to validate that the API is working as expected:

  $ curl -X GET
"https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/fizzbuzz/3"
{"answer":"fizz"} $ curl -X GET
"https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/fizzbuzz/5"
{"answer":"buzz"}
$ curl -X GET "https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/fizzbuzz/7" {"answer":7}

Notice that unlike the first example, which just invoked the top-level API endpoint, we’re passing data to the API in the form of a URL parameter defined as “number” in the function. You can deploy much more extensive, interactive APIs by utilizing parameters.

Let’s say we want to inspect the data we pass to the API in a given request. Chalice provides helpful syntax for inspecting requests, utilizing the “to_dict()” method.

Go ahead and add a new route and function to “app.py” with the following code:

 @app.route('/inspect_request', methods=['POST'])
  def post_headers():
     return app.current_request.to_dict()

Note the “methods” parameter that’s been added. This function will only respond to POST requests, rather than GET or PUT requests. Run a deploy again:

 $ chalice deploy

Now, query the “/inspect_request” endpoint, making sure to specify a POST method:

  $ curl -X GET 
	https://xxxxxxxxxx.execute-api.eu-west-2.amazonaws.com/inspect_request

You should receive a JSON response containing all the data passed in your request to the endpoint.

What if you want to inspect what your web browser passes instead? Enter the request URL in your browser:

 {"Code":"MethodNotAllowedError","Message":"Unsupported method: GET"}

Whoops! Browser requests are almost always GET requests, and our function isn’t properly configured for that yet. So, update the “inspect_request” function as follows:

 @app.route('/inspect_request', methods=['POST', 'GET'])
 def post_headers():
     return app.current_request.to_dict()

Run another deploy:

 $ chalice deploy

And visit the URL with your browser again. Now you should be able to see all the data that your browser passes in a request. Awesome! For cleanup, simply run:

 $ chalice delete

Chalice handles removing all the provisioned resources. That’s it, you’re done!

Exploring Additional Features

If you want to test out changes locally, without invoking changes to AWS-hosted infrastructure, Chalice provides a local development server. Simply run the command:

 $ chalice local 

Now, you have a near fully featured endpoint available at:

 http://127.0.0.1:8000/


Any routes and functions that are present in “app.py” can be utilized at the local endpoint and will behave as if they are live in AWS. The development server provides much more verbose logging to troubleshoot any issues your live code might have.

Chalice also supports integrations with Amazon SQS, Amazon SNS, Amazon S3, and Amazon CloudWatch Events. You can set triggers for your Lambda functions to be invoked by any of the aforementioned services.

Making Serverless Easy

AWS Chalice is an easy-to-use framework that enables developers to quickly spin up fully featured REST APIs, backed by Python-based Lambda functions. Rather than having to cope with configuring and deploying cloud resources, developers can have their stack up and serving requests with just a few simple commands.