Boilerplate Hasura project with Flask microservice.
- Hasura CLI
- Git
- Python 3 and pip (required only for local development)
# Quickstart from this boilerplate
$ hasura quickstart hello-python-flask
The quickstart
command does the following:
- Creates a new directory
hello-python-flask
in the current working directory - Creates a free Hasura cluster and sets it as the default for this project
- Sets up
hello-python-flask
as a git repository and addshasura
remote to push code - Adds your SSH public key to the cluster so that you can push to it
# Navigate to the project directory
$ cd hello-python-flask
# git add, commit and push to deploy
$ git add . && git commit -m "First commit"
$ git push hasura master
Once the git push goes through, Flask microservice (called app
) will be available at a URL.
# Open the flask app url in browser
$ hasura microservice open app
If the browser shows a "Hasura Hello World" page, everything is working as expected. If it doesn't, go through the previous steps and see if you missed anything.
The flask microservice is located in microservices/app
directory in your Hasura project with the following structure:
.
├── Dockerfile # instructions to build the image
├── k8s.yaml # defines how the app is deployed
├── conf
│ └── gunicorn_config.py # configuration for the web server
└── src
├── config.py # some utilities to configure URLs etc
├── hasura.py # hasura API examples
├── __init__.py # main Flask app is defined here
├── requirements.txt # python dependency requirements
└── server.py # main Flask server code
server.py
is where the main app is present. You can edit this file and deploy the changes.
For example, un-comment lines 2
, 11-13
to add new URL /json
:
from flask import jsonify
@app.route("/json")
def json_message():
return jsonify(message="Hello World")
These lines will add /json
which returns {"message": "Hello World"}
.
Save the file, git add, commit and push to deploy the changes:
# git add, commit and push to deploy
$ git add src/server.py
$ git commit -m "add new url /json"
$ git push hasura master
To checkout the new URL, open the microservice URL in a browser and navigate to /json
:
# open the url in browser
$ hasura microservice open app
# add /json at the end of the url
If the push fails with an error Updating deployment failed
, or the URL is showing 502 Bad Gateway
/504 Gateway Timeout
,
follow the instruction on the page and checkout the logs to see what is going wrong with the microservice:
# see status of microservice app
$ hasura microservice list
# get logs for app
$ hasura microservice logs app
You can deploy further changes by going through Edit -> Deploy -> Verify -> Debug
cycle again and again.
TIP: If you already have a working flask app and want to deploy it quickly, jump to Deploy your existing Flask app
Hasura comes with a pre-configured ready-to-use PostgreSQL database, which can be contacted over HTTP JSON APIs. You can use this database from client side or server side just by making HTTP API calls with JSON data. There are no DB connection strings or ORMs to worry about.
The best place to get started with APIs is Hasura API Console.
$ hasura api-console
This command will run a small web server and opens up API Console in a new browser tab.
There are already some tables created along with this boilerplate, like article
and author
.
These tables were created using migrations.
Every change you make on the console is saved as a migration inside migrations/
directory.
- Create required tables/columns using API Console (Data -> Schema)
- Use Query Builder under API Explorer and create the query
- Click on Generate API Code button and select Python Requests
- Copy and paste the python code into your flask app source code
This boilerplate defines a /get_articles
endpoint which fetches a list of articles from the database and return JSON data. The query builder will show similar code when same query is built.
# hasura.py
@hasura_examples.route("/get_articles")
def get_articles():
query = {
"type": "select",
"args": {
"table": "article",
"columns": [
"*"
]
}
}
response = requests.post(
dataUrl, data=json.dumps(query)
)
data = response.json()
return jsonify(data=data)
This snippet of code returns a JSON similar to the following format:
{
"data": [
{
"author_id": 12,
"content": "Vestibulum accumsan neque et nunc. Quisque...",
"id": 1,
"rating": 4,
"title": "sem ut dolor dapibus gravida."
},
{
"author_id": 10,
"content": "lacus pede sagittis augue, eu tempor erat neque...",
"id": 2,
"rating": 4,
"title": "nonummy. Fusce fermentum fermentum arcu."
},
...
]
}
If you look closer in hasura.py
, dataUrl
is defined in two different ways, internal and external. When your app is running inside the cluster, it can directly contact the Data APIs without any authentication. On the other hand, external URLs always go through the API Gateway, and hence special permissions will have to be applied over table for a non-authenticated user to access data.
http://data.hasura
- internal urlhttp://data.[cluster-name].hasura-app.io
- external url
PS: Hasura Data APIs are really powerful with nifty features like relationships, role based row and column level permissions etc. Using the APIs to their full potential will prevent you from re-inventing the wheel while building your app and can save a lot of time.
TIP: Use hasura ms list
to get all internal and external URLs available in your current cluster.
When your app needs authentication, Hasura Auth APIs can be used to manage users, login, signup, logout etc. You can think of it like an identity service which takes away all the user management headaches from your app so that you can focus on your app's core functionality.
Just like the Data APIs, you can checkout and experiment with the Auth APIs using Hasura API Console.
Combined with database permission and API gateway session resolution, you can control which user or what roles have access to each row and column in any table.
For every request coming through external URL into a cluster, the API gateway tries to resolve a user based on a Cookie
or an Authorization
header. If none of them are present or are invalid, the following header is set and then the request is passed on to the upstream service:
X-Hasura-Role: anonymous
But, if the cookie or the authorization header does resolve to a user, gateway gets the user's ID and role from auth microservice and add them as headers before passing to upstream:
X-Hasura-User-Id: 3
X-Hasura-Role: user
Hence, other microservices need not manage sessions and can just rely on X-Hasura-Role
and X-Hasura-User-Id
headers.
Hasura runs microservices as Docker containers on a Kubernetes cluster. You can read about Hasura architecture in case you want to know more.
In order use new python package in your app, you can just add it to src/requirements.txt
and the git-push or docker build process will
automatically install the package for you. If the pip install
steps thorw some errors in demand of a system dependency,
you can install those by adding it to the Dockerfile
at the correct place.
# src/requirements.txt:
flask
requests
gunicorn
# add your new packages one per each line
The base image used in this boilerplate is python:3 debian. Hence, all debian packages are available for installation.
You can add a package by mentioning it in the Dockerfile
among the existing apt-get install
packages.
# Dockerfile
FROM python:3
# install required debian packages
# add any package that is required after `python-dev`, end the line with \
RUN apt-get update && apt-get install -y \
build-essential \
python-dev \
&& rm -rf /var/lib/apt/lists/*
# install requirements
COPY src/requirements.txt /tmp/requirements.txt
RUN pip3 install -r /tmp/requirements.txt
# set /app as working directory
WORKDIR /app
# copy current directory to /app
COPY . /app
# run gunicorn server
# port is configured through the gunicorn config file
CMD ["gunicorn", "--config", "./conf/gunicorn_config.py", "src:app"]
If you already have a Flask app and want to deploy it onto Hasura, read ahead:
- Replace the contents of
src/
directory with your own app's python files. - Leave
k8s.yaml
,Dockerfile
andconf/
as it is. - Make sure there is already a
requirements.txt
file present inside the newsrc/
indicating all your python dependencies (see above). - If there are any system dependencies, add and configure them in
Dockerfile
(see above). - If the Flask app is not called
app
, change the last line inDockerfile
reflect the same. For example, if the app is calledbackend
, theCMD
line inDockerfile
will become:CMD ["gunicorn", "--config", "./conf/gunicorn_config.py", "src:backend"]
With Hasura's easy and fast git-push-to-deploy feature, you hardly need to run your code locally. However, you can follow the steps below in case you have to run the code in your local machine.
It is recommended to use a Virtual Environment for Python when you are running locally.
Don't forget to add these directories to .gitignore
to avoid committing packages to source code repo.
# setup pipenv or virtualenv and activate it (see link above)
# go to app directory
$ cd microservices/app
# install dependencies
$ pip install -r src/requirements.txt
# Optional: set an environment variable to run Hasura examples
# otherwise, remove Hasura examples,
# delete lines 5-8 from `src/__init__.py`
# remove file `src/hasura.py`
$ export CLUSTER_NAME=[your-hasura-cluster-name]
# run the development server (change bind address if it's already used)
$ gunicorn --reload --bind "0.0.0.0:8080" src:app
Go to http://localhost:8080 using your browser to see the development version on the app. You can keep the gunicorn server running and when you edit source code and save the files, the server will be reload the new code automatically. Once you have made required changes, you can deploy them to Hasura cluster.
Install Docker CE and cd to app directory:
# go to app directory
$ cd microservices/app
# build the docker image
$ docker build -t hello-python-flask-app .
# run the image with port bindings and CLUSTER_NAME environment variable
# as mentioned above, remove hasura.py if you don't want to add CLUSTER_NAME
$ docker run --rm -it -p 8080:8080 -e CLUSTER_NAME=[your-hasura-cluster-name] hello-python-flask-app
# app will be available at `http://localhost:8080`
# press Ctrl+C to stop the running container
For any change you make to the source code, you will have to stop the container, build the image again and run a new container. If you mount the current directory as a volume, you can live-reload your code changes:
# go to app directory
$ cd microservices/app
# build the docker image
$ docker build -t hello-python-flask-app .
# run the container
$ docker run --rm -it -p 8080:8080 \
-e CLUSTER_NAME=[your-hasura-cluster-name] \
-v $(pwd):/app \
hello-python-flask-app \
gunicorn --reload --bind "0.0.0.0:8080" src:app
# app will be available at `http://localhost:8080`
# press Ctrl+C to stop the running container
Now, any change you make to your source code will be immediately updated on the running app.