[PYTHON] I touched AWS Chalice

What is AWS Chalice?

Amazon API Gateway and AWS Lambda are semi-automatically clicked from the CLI. It seems that you can make it. I like AWS Lambda (and Amazon API Gateway) so I touched it.

Preparation

Creating a virtual environment for Python

It is recommended to create a virtual environment with AWS Chalice Hands-on, so create it as needed. I will.

Install Virtualenv to create a virtual environment

$ pip install virtualenv

Creating a virtual environment

# 『~/.virtualenvs/chalice-"handson" is the environment name
#It's just for AWS hands-on, so you can use any environment name.
virtualenv ~/.virtualenvs/chalice-handson

Enable virtual environment

$ source ~/.virtualenvs/chalice-handson/bin/activate
#"Source" is ".] Can be substituted, so the following command has the same meaning as above.
$ . ~/.virtualenvs/chalice-handson/bin/activate

End of virtual environment

If you want to terminate the virtual environment, use the following command. Even if you exit the virtual environment, the created virtual environment file remains, so you can enter the virtual environment by typing the enable command again.

$ deactivate

Chalice installation

$ pip install chalice
#Check if it was installed
$ chalice --version
chalice 1.12.0, python 3.7.3, darwin 19.6.0

AWS credentials

If ~ / .aws / credentials and ~ / .aws / config do not exist, set them with the following command. Otherwise you will get angry when deploying to AWS.

$ aws configure

Create a new project

Now that we're ready, let's take a quick look at the flow from project creation to deployment to AWS. First, create a project with the following command.

#"Helloworld" is the project name used in AWS hands-on, and any project name can be specified.
$ chalice new-project helloworld

The following files are created by creating a project.

.
├── .chalice
│   └── config.json
├── .gitignore
├── app.py
└── requirements.txt

The contents of the automatically created ʻapp.py` are as follows. The content is simple, it returns {'hello':'world'} as a response body when accessing the index of the API Gateway endpoint. The response body {'hello':'world'} is set with {'hello':'world'} even if the project name is not helloworld.

from chalice import Chalice

app = Chalice(app_name = 'helloworld')

@app.route('/')
def index():
    return {'hello': 'world'}    #Even if the project name is other than helloworld, here{'hello': 'world'}is

Local testing

Try it in your local environment to see if it actually behaves as described above (does it return {'hello':'world'} when accessed)? You can start the local server with the following command. : + 1: Easy and nice!

$ chalice local
Serving on http://127.0.0.1:8000

If you try to access it with a browser, you should see {"hello ":" world "}.

Deploy to AWS

It looks okay, so let's deploy it to AWS so that it can be accessed from outside. Deploying is easy, just run the following command.

$ chalice deploy

Then, the following will be created automatically on AWS.

--IAM roll --Lambda function

After deploying, the configuration will be as follows.

.
├── .chalice
│   ├── config.json
│   ├── deployed
│   │   └── dev.json
│   └── deployments
│       └── xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-python3.8.zip
├── .gitignore
├── app.py
└── requirements.txt

Finally, the API Gateway endpoint is displayed as Rest API URL, so let's access it with a browser. If you don't know the Rest API URL, you can display it with the following command.

$ chalice url

Request handling

Now that you've seen the basics of using AWS Chalice, let's look at some more control features.

URL parameters

Parameters can be described in the path pattern of @ app.route ('/') of ʻapp.py` as shown below, and the value received as a parameter can be used as an argument in the method.

from chalice import Chalice

app = Chalice(app_name = 'helloworld')

@app.route('/')
def index():
    return {'hello': 'world'} 

#Add the following
@app.route('/hello/{name}')
def greet(name):
    return {'hello': name}

After editing ʻapp.py, do chalice deployagain. After the deployment is complete, add/ hello /" arbitrary value "to the API Gateway endpoint and access it with a browser. You should see{" hello ":" "arbitrary value" "}`. Please note that the value passed as "arbitrary value" is a character string even if it is a numerical value.

HTTP method

HTTP method can be specified by writing the method in @ app.route ('/') of ʻapp.py` as follows.

from chalice import Chalice

app = Chalice(app_name = 'helloworld')

@app.route('/')
def index():
    return {'hello': 'world'}

@app.route('/hello/{name}')
def greet(name):
    return {'hello': name}

#Add the following
@app.route('/hello', methods = ['POST'])
def hello():
    return {'hello': 'POST world'}

After editing ʻapp.py, do chalice deploy` again. The POST added this time cannot be confirmed from the browser, so let's check it quickly in Python's interactive mode.

$ python
>>> import requests    #If requests are not included, pip install requests
>>> response = requests.post('"API Gateway Endpoint"/hello', data = {})
>>> print(response.text)
{"hello":"POST world"}

Access metadata

The code from here will only describe the change block and response. Metadata acquisition is specified in the form of ʻapp.current_request." Honyara "`. HTTP Method

@app.route('/hello')
def hello():
    return {'metadata': app.current_request.method}

#response
{"metadata":"GET"}

Query Parameters

@app.route('/hello')
def hello():
    return {'metadata': app.current_request.query_params}

#Response (request is/hello?test1=abc&test2=123&test2=456)
#If a parameter with the same name is specified in the request, chalice will win second (test2)=123&test2=456 is test2=Become 456)
{"metadata":{"test1":"abc","test2":"456"}}

Request Body - Raw Get the request body in byte type. If Content-Type: application / json, you can use ʻapp.current_request.json_body instead of ʻapp.current_request.raw_body. This is a character string type.

@app.route('/hello')
def hello():
    return {'metadata': app.current_request.raw_body}

#request
$ python
>>> import requests, json
>>> response = requests.post('"API Gateway Endpoint"/hello', data = json.dumps({'hello': 'world'}), headers = {'Content-Type': 'application/json' })
>>> print(response.text)
#response
{"metadata":{"hello":"world"}}

Response handling

Next, let's look at the response.

Custom HTTP response

If you want to return an arbitrary status code or header, return it with arbitrary information included in the response class object. Don't forget ʻimport Response. (It doesn't matter, but I wrote json.dumps without json.dumpands, and {“Code”: “InternalServerError”, “Message”: “An internal server error occurred.”} I got a response of `and I was addicted to it for a while without noticing the mistake ...: persevere :)

from chalice import Chalice, Response
import json

app = Chalice(app_name='helloworld')

@app.route('/')
def index():
    return Response(
        body = json.dumps({'hello':'world'}),
        headers = {'Content-Type': 'application/json'},
        status_code = 200
    )

@app.route('/text')
def text():
    return Response(
        body = 'Hello, World',
        headers = {'Content-Type': 'text/plain'},
        status_code = 200
    )

Error HTTP response

Since there is a class for returning an error response, we will use it. The example below is a 403 Forbidden Error. Don't forget to import the error class here as well.

from chalice import Chalice, ForbiddenError

app = Chalice(app_name='helloworld')

@app.route('/forbidden')
def forbidden():
    raise ForbiddenError(
        '403!'
    )

There are other error classes as follows. ([[Learn AWS with Easy Hands-on] Build a Serverless RESTful API! Python App Development Realized with Chalice | AWS Startup Blog](https://aws.amazon.com/jp/blogs/startup/event] See -report-chalice-handson /)) It is also possible to return a response with an error code that is not prepared by using the custom HTTP response introduced above. (Of course, it is also possible to return an error with an error code in a custom HTTP response.)

Enable CORS

You can simply write the following.

@app.route('/', methods = ['POST'], cors = True)

Decorator

I've always written it in the form of @ app.route ('/'), but this is in the form of API Gateway + AWS Lambda. AWS Lambda integration other than API Gateway is possible by using other decorators. The following are currently supported.

--Independent AWS Lambda: @ app.lambda_function

For detailed usage and sample code, I tried to manage Lambda (Python) and API Gateway with Chalice is very helpful.

Delete

IAM roles, Lambda, and API Gateway (for @ app.route) deployed on AWS with chalice deploy can be deleted in bulk with the following command.

$ chalice delete

Summary

It seems that you can build a serverless environment quite easily using AWS Chalice. It's easy to delete, so it's nice to be able to easily create and delete it. I was thinking of writing a more concrete example, but since it has already become a long article, I will summarize it in another article.

We're hiring! We are developing an AI chatbot. If you are interested, please feel free to contact us from the Wantedly page!

Reference article

-[Easy hands-on learning AWS] Build a serverless RESTful API! Python application development realized by Chalice | AWS Startup Blog

Recommended Posts

I touched AWS Chalice
I tried using AWS Chalice
I touched HaikuFinder
I touched Flask
I tried AWS Iot
I touched the Qiita API
I touched Wagtail (2). Introducing django-extensions.
I touched Tensorflow and keras
I touched PyAutoIt for a moment
Determine if AWS Chalice is chalice local
I touched something called Touch Designer
I made a new AWS S3 bucket
I touched "Orator" so I made a note
I am making my own aws cli
I want to play with aws with python
3 small stories I checked while using Chalice
I touched the data preparation tool Paxata
I just did FizzBuzz with AWS Lambda