Flask API & Cognito
Our API uses API Gateway and Lambda functions with Python and a library called Flask. It’s a great match for our uses and it helps us speed up the development process for our API business logic.
We will use Cognito to protect our API endpoints; and thankfully, there are many resources that guide us in this endeavor.
Requirements
The python requirements are organized in three environments:
** TO DEVELOP LOCALLY YOU MUST CHOOSE DEV **
Install the requirements in your machine, run these in order:
This particular requirements file includes tools such as pytest that make development and unit testing a lot easier, but it also makes the api bulky. Do not bother in installing the production or staging requirement files, those are only meant for cloud deployments.
Running the API (with hot-reload)
Once the installation of the requirements is done, you are ready to launch the application using this command:
You may have noticed the FLASK_ENV=development
bash variable, this is passed to the flask
command and it will initialize the application in app.py
and enabled hot-reload, meaning that any changes you make to the code will be automatically reloaded for you (without you having to restart the API for every change).
Blueprint Architecture
We will adhere to a blueprint architecture as it is stipulated in their documentation: https://flask.palletsprojects.com/en/1.1.x/blueprints/#blueprints
This is going to help scale large amounts of code into our API, it should also help with modularity and code re-use and our testing strategies. Please refer to the architecture notes below for details on how blueprints work.
Test-driven development
To enable test-driven development patterns in our API I have created a tests folder with a sample test.
In the API root directory, you can use these commands to run your tests:
Creating a new test file
You should look at a file called ./tests/test_app.py
and copy it into a new file. Inside the test_app.py file you will see this syntax:
First you need to make sure you import the Flask application:
Then, you create a test class, it must begin with the prefix Test
in order to be valid, here we use TestApp
but if you were to create a new test file for say the authentication blueprint, you could name the test class TestAuth
so on and so forth:
And your first test could be something like this:
Parsing the JWT token within the API
Parsing JWT tokens provided by AWS Cognito is done with the help of the flask-cognito library (see references at the bottom) Take a look at the ./auth/auth.py
file in the API, you will notice a few interesting lines:
First we import two helper methods:
cognito_auth_required: This is a decorator that populates the value of a global thread variable (local) with the decoded JWT token.
current_cognito_jwt: This is a helper function, basically it can safely access the place in memory where the JWT token is stored. This is a lambda function that returns a class of type LocalProxy, which wraps the dictionary value we are looking for. To access the decoded token value as a dictionary, use the
_get_current_objec()
method.
Example:
Custom Decorators
One thing I have discovered with Hasura and Cognito, is that the Hasura claims are not a normal JSON document, in fact, the JWT token wraps the Hasura claims in a nested JSON (JSON within a JSON), which can be inconvenient, but it is the way it works according to their documentation.
This is an example of a decoded JWT token, notice the Hasura claims in the https://hasura.io/jwt/claims
key:
To help with this, I’ve created a couple decorators and methods in the ./claims.py
file, which can help with obtaining a normalized version of the token. One of them is called @normalize_claims
, here is an example on how to use it:
You will notice there will not be any nested JSON strings. Feel free to implement your own decorators or helper methods.
Diagram owner
Team
@ Team member
Informed
@ Stakeholder
@ Stakeholder
Status
/ / /
Last date updated
e.g.,24 Sep 2020
On this page
Name
Description
Operational Excellence
The ability to run and monitor systems to deliver business value and to continually improve supporting processes and procedures.
Security
The ability to protect information, systems, and assets while delivering business value through risk assessments and mitigation strategies.
Reliability
The ability of a system to recover from infrastructure or service disruptions, dynamically acquire computing resources to meet demand, and mitigate disruptions such as misconfigurations or transient network issues.
Performance Efficiency
The ability to use computing resources efficiently to meet system requirements, and to maintain that efficiency as demand changes and technologies evolve
Cost Optimization
The ability to run systems to deliver business value at the lowest price point.
AWS Well Architected Framework PDF
note
Goals
Secure the API to Cognito users only.
Modular and scalable development of the API with Flask Blueprints
Test-driven development
Access to JWT tokens.
Secure the API to Cognito users only.
Modular and scalable development of the API with Flask Blueprints
Test-driven development
Access to JWT tokens.
Architecture
It must be noted that currently PRs are not supported, this is because a branch would need to be deployed, and each branch will require specific configurations and resources in AWS to work properly. It may be possible to group together all the resources zappa creates, and then produce a single configuration for all branches and make endpoints using common permissions and configurations. Again, at the moment PRs are not yet supported.
Architecture & Blueprints
To implement the API we will strictly follow the blueprint guidelines in the Flask documentation: https://flask.palletsprojects.com/en/1.1.x/blueprints/#blueprints
These are the main takeaways about our architecture:
app.py
, this is the entrypoint for all API calls, and in a way, it is the main router. It’s job is to instantiate the main application, and it is here where we register all the other sections (blueprints) we will implement in the future.config.py
contains any configuration needed by app.pyclaims.py
currently contains helpers that can help decoding JWT tokens, and its purpose it to implement decorators that can be re-used throughout the project.tests
contains all the tests for this api following pytest patterns.requriements
contains the python libraries needed to run locally (dev) or remotely (staging, production).static
contains any static files served by app.pytemplates
contains any jinja templates used by app.py<blueprints> each blueprint will be hosted in a folder
Take a look at the root directory of the API, it should look a little bit like this:
Blueprints
When creating a new blueprint, you will need to create a new folder. Take a look at auth
for example, at the time of writing this, it looks a bit like this:
The pattern should be like this:
<blueprint name as folder>/<blueprint name as python file> => auth/auth.py
Every blueprint directory should contain a templates directory that contains any jinja templates that are used specifically by that blueprint. Any other code related to the blueprint should be contained in the same directory.
Deployment strategy
Rest API endpoints
The REST API routes and functionality will be described later on in the documentation; for now, these are the endpoint URL addresses.
Staging: https://moped-api-staging.austinmobility.io
Production: https://moped-api.austinmobility.io
Making a test request:
The first thing you will need to make a request is to get a valid JWT token, if you are in Staging you will need a staging token, if you are going to ping the production endpoint you will need a production JWT.
To get a token, log in to the site (staging):
https://moped.austinmobility.io/moped/session/signin
Copy that token and paste it in curl like this:
Action Items
Action
Description
Owner
Due date
ZenHub ticket
1
Implement support for PR for APIs
Eventually, we will need PRs for staging, we need to architect this eventually.
e.g.,24 Sep 2020
Add your Jira ticket by typing / Jira.
2
References and documentation
Zappa Framework https://github.com/Miserlou/Zappa
Flask Cognito: https://github.com/jetbridge/flask_cognito
Last updated