Flask + Serverless — API in AWS Lambda the Easy Way
Tags: python, flask, aws, api
Published: 2022-07-06 06:33:12

If you either need to quickly deploy a small API or just decided to migrate your codebase to leverage the advantages of AWS Lambda. There are of course other guides on how to accomplish that, like packing and uploading your app by yourself. The process gets a bit more tricky when you realize some dependencies are not compatible (because of Linux on lambda) and you need to handle that too. 


## Solution
Use a powerful combo of Flask and Serverless framework. Any WSGI application such as Django can work too. We will use a quotes service. 


## Recipe

1. Download and install [Python](https://www.python.org/downloads/windows/) in your device, then install pip and virtualenv package.
    
    ```console
      pip install virtualenv
    ```


2. Install Serverless framework, [see full instructions](https://codesandbox.io/s/i18n-react-5t3vxk)

    ```
      $ npm install -g serverless
    ```

2. Create your main folder, can call it "quotes". Then create the python venv.

     ```console
        mkdir quotes
        cd quotes
        virtualenv venv -p python3
        (venv) pip install Flask
        (venv) pip freeze > requirements.txt
    ```
    
    You will now have a venv folder, go to quotes/venv/Scripts and run "activate" to active the virtual environment.

3. At the main folder /quotes create an app.py file to contain our quotes service,then add the information below.

    ```python
        from collections import namedtuple
        from random import choice
        
        from flask import Flask, jsonify
        
        Quote = namedtuple("Quote", ("text", "author"))
        
        quotes = [
            Quote("Talk is cheap. Show me the code.", "Linus Torvalds"),
            Quote("Programs must be written for people to read, and only incidentally for machines to execute.", "Harold Abelson"),
            Quote("Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live",
                "John Woods"),
            Quote("Give a man a program, frustrate him for a day. Teach a man to program, frustrate him for a lifetime.", "Muhammad Waseem"),
            Quote("Progress is possible only if we train ourselves to think about programs without thinking of them as pieces of executable code. ",
                "Edsger W. Dijkstra")
        ]
        
        app = Flask(__name__)
        
        @app.route("/")
        def hello_from_root():
            return jsonify(message='Hello from root!')

        @app.route("/quote", methods=["GET"])
        def get_random_quote():
            return jsonify(choice(quotes)._asdict())
    ```
4. Wire it with the Serverless framework. To do so we need to create a serverless.yml file (in our root) manually. The file will look like this:

    ```yml
        service: quotes #name this whatever you want
        
        provider:
        name: aws
        runtime: python3.6
        region: us-east-1
        memorySize: 128
    ```

5. Install two Serverless plugins. The first thing we need is to make Lambda understand WSGI (protocol Flask/Django is using), second is to make Serverless pack our python requirements into our deployment package.

    ```console
      $ sls plugin install -n serverless-wsgi
      $ sls plugin install -n serverless-python-requirements
    ```

6. Update the serverless.yml file with the plugins and further information needed.

    ```yml
    service: quotes-serverless #name this whatever you want
 
    provider:
    name: aws
    runtime: python3.6
    region: us-east-1
    memorySize: 128

    plugins:
    - serverless-wsgi
    - serverless-python-requirements

    custom:
    wsgi:
    app: app.app
    packRequirements: false

    functions:
    app:
    handler: wsgi_handler.handler
    events:
        - httpApi: '*'

    deprecationNotificationMode: error #add this for debugging
    configValidationMode: error #add this for debugging
        `);

    ```
    You can see a new section (custom) which holds configuration for those plugins. The wsgi part is saying where your app is and turning off packing of requirements. The last part (functions) declares what our service contains. We can have more functions within one service and also require specific permissions. In this case, we are just saying that all requests will be served through a WSGI handler, which is provided by our installed plugin.

7. Update requirements.txt
    
    ```json
    Flask==1.1.4
    Werkzeug==1.0.1
    markupsafe==2.0.1
    ```

    Check 'Werkzeug' version, can cause error while deploying.

8. Do a Local deployment to test the service, run:

    ```console
        $ serverless wsgi serve
    ```

    You can open the [server](browser_local.png) to the specific path, for example http://localhost:5000/

    > use /quotes to see the random quotes.


8. Before deployment our project to AWS you need to [create an account](https://aws.amazon.com/).

    1. Login to your AWS account and go to the Identity & Access Management (IAM) page.

    2. Click on Users and then Add user. Enter a name in the first field to remind you this user is related to the Serverless Framework, like serverless-admin. Enable Programmatic access by clicking the checkbox. Click Next to go through to the Permissions page. Click on Attach existing policies directly. Search for and select AdministratorAccess then click Next: Review. Check to make sure everything looks good and click Create user.

    3. View and copy the API Key & Secret to a temporary place. These are your AWS access keys.

    > see more [instruction in here](https://www.serverless.com/framework/docs/providers/aws/guide/credentials#create-an-iam-user-and-access-key) 

9. Add AWs credentials to the project, the easiest way is create an .env file in our root folder and add the information below.
    
9. Edit the create a new file nodemon.json in the root of your project folder, that file will be used to store our environment variables. In case that you are not using  nodemon , create an .env file.

    ```env
        set AWS_ACCESS_KEY_ID=<your-key-here>
        set AWS_SECRET_ACCESS_KEY=<your-secret-key-here>
        # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are now available for serverless to use
        serverless deploy

        # In Linux use 'export' instead of 'set'
    ```

10. Deploy our project, run the below command.

    ```
        $ serverless deploy
    ```

    Now we can see the server in our [endpoint](https://17xjghp0c9.execute-api.us-east-1.amazonaws.com/quote), at AWS Cloudformation we now have our Stack of the deployed project.

 > If you get an internal error or want to see recent logs, open CloudWatch in your AWS console or just type: serverless logs -f app  


Feel free to visit this repository with all what this blog [contains](https://github.com/ICCHA-Technologies/Flask-Serverless-API-Cookbook).