Up until now, I have mostly been developing new Flask projects from scratch. As most projects tend to consist of similar folder structures, it gets really mundane setting up the identical base projects repeatedly over a period of time.
I have been using Docker for some time now, and with the capability that it enables, we are able to create a starter-kit image with required core functionalities implemented, that can be served almost instantaneously.
Before we get started, let’s talk about our objective first.
We are trying to develop a starter-kit project that runs on Flask, that has the following functionalities:
Let’s begin!
For this tutorial, we will need:
- Python (I'll be using Python 3.7)
- Pip
- Virtualenv
- Docker
Flask is a micro framework for Python based on Werkzeug, Jinja 2 and good intentions. — Flask Documentation
Setting up a barebones Flask project is really simple. However, since there’s quite a bit of functionality that we want to be built in, the following steps will take slightly longer than usual.
Start by creating a virtual environment.
$ virtualenv venv
$ source venv/bin/activate
Next, we can install the required packages. As to not confuse everyone, I’ll include only the required packages for each step respectively.
$ pip install flask flask_script
$ mkdir -p app/__init__.py
$ touch manage.py
To future proof ourselves, and to make our project scalable, we will be following the Application Factory Pattern. I will not go too deep into the details of “Why” in this article. But the gist of it would be that it provides us with
You can read more about Application Factories Pattern here.
We will start by creating our Flask app file and implementing the basic flask functionality.
The create_app()
function is a simple example of how the Application Factories Pattern is implemented.
“`
<br><br>
<h1>app/<strong>init</strong>.py</h1>
<br><br>
“`
from flask import Flask
“`
<br><br>
def create_app():<br> app = Flask(<strong>name</strong>)
<br><br>
<pre><code>@app.route("/")<br>def hello_world():<br> return "Hello World!"<br></code></pre>
<br><br>
“`
return app
The Flask-Script extension provides support for writing external scripts in Flask. This includes running a development server, a customised Python shell, scripts to set up your database, cronjobs, and other command-line tasks that belong outside the web application itself.
With reference to the Flask-Script documentations , we will also add the following codes to our ./manage.py
file.
“`
<br><br>
<h1>./manage.py</h1>
<br><br>
“`
from flask_script import Server, Manager
from app import create_app
“`
<br><br>
app = create<em>app()<br>manager = Manager(app)<br>manager.add</em>command("runserver", server)<br>“`
“`
<br><br>
if <strong>name</strong> == "<strong>main</strong>"):<br> manager.run()<br>“`
After setting up these files, we can start our application by running the command python ./manage.py runserver
.
At this point, let’s do a pip freeze >> requirements.txt
.
It’s always a good habit to do pip freeze
after installing a new package and you are certain that you will be using it in your new project . It allows you to reinstall all the required dependencies in a different development environment.
Before you proceed further, do ensure that you have Docker installed.
If you are unfamiliar with Docker, I highly recommend that you give this article by Jeff Hale a read.
Now that we have a basic “Hello World” Flask application setup, we can follow up by containerizing our application with Docker.
Start by creating a file called Dockerfile
in our root folder. This file contains the instruction for Docker to execute while building up our Flask project image. It allows us to spin up similar instances with a single command.
“`
<br><br>
<h1>Dockerfile</h1>
<br><br>
“`
“`
<br><br>
<h1>Using the official Python 3.7 image</h1>
<br><br>
FROM python:3.7<br>“`
“`
<br><br>
<h1>Set the Work Directory</h1>
<br><br>
WORKDIR /usr/src/app<br>“`
“`
<br><br>
<h1>Copy over the requirements.txt file</h1>
<br><br>
COPY ./requirements.txt /usr/src/app/requirements.txt<br>“`
“`
<br><br>
<h1>Install the project's dependencies</h1>
<br><br>
RUN pip install -r requirements.txt<br>“`
“`
<br><br>
<h1>Copy the project codes into the Work Directory</h1>
<br><br>
COPY . /usr/src/app<br>“`
“`
<br><br>
<h1>Expose port so that it's accessible to external connections</h1>
<br><br>
EXPOSE 5000<br>“`
“`
<br><br>
<h1>Run the Flask application</h1>
<br><br>
CMD ["python", "manage.py", "runserver"]<br>“`
With this, we can actually start running our application through the Docker container.
$ docker image build -t flask-starter-kit:latest .
$ docker container run -p 5000:5000 flask-starter-kit
After running the commands, you can once again access the flask server at http://localhost:5000.
Docker Compose is a tool for defining and running multi-container Docker applications. With it, we can serve multiple services at once as well as link multiple Docker images with each other.
We can configure a YAML file docker-compose.yml
in our root folder that contains all the information of our multiple services, and how each image links with each other.
Then, with a single command, we can create and start all the services from the configuration.
We can set up our Flask service to link to whichever database service that we require, such as PostgreSQL, NoSQL, etc. In this case, we will go with MySQL.
“`
<br><br>
<h1>docker-compose.yml</h1>
<br><br>
“`
version: "3"
services:
server:
container_name: flask
build: .
ports: ['5000:5000']
volumes: ['.:/usr/src/app']
restart: always
environment:
ENV: DEVELOPMENT
DB_USERNAME: username
DB_PASSWORD: password
DB_DATABASE: database_name
DB_HOST: localhost
links:
- database
database:
container_name: database
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: database_name
MYSQL_USER: username
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
ports:
- '3306:3306'
expose:
- '3306'
volumes:
- flask-db:/var/lib/mysql
“`
volumes:
flask-db:
<br><br>
“`
With this configuration, when we run the command docker-compose up
, Docker will serve both our Flask application, as well as a connected MySQL service. We can then start adding code to integrate our Flask application with the MySQL service.
We will first have to install the required dependencies
$ pip install flask-sqlalchemy pymysql
We can now start to set up our Flask app to connect with our MySQL service. For that, we will have to add a config.py
file and also update our app/__init__.py
file.
Using a config file, we are able to elegantly separate our environment variables, as well as store our constant variables as required.
“`
<br><br>
<h1>./config.py</h1>
<br><br>
“`
import os
“`
<br><br>
class Config(object):<br> ENV = os.environ['ENV']<br> CSRF<em>ENABLED = True<br> SECRET</em>KEY = "this<em>is</em>a<em>secret</em>key"
<br><br>
“`
# Database Configuration
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://' + os.environ['DB_USERNAME'] + ':' + os.environ['DB_PASSWORD'] + '@' + os.environ['DB_HOST'] + ":3306/" + os.environ['DB_DATABASE']
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://' + os.environ['DB_USERNAME'] + ':' + os.environ['DB_PASSWORD'] + '@' + os.environ['DB_HOST'] + ":3306/" + os.environ['DB_DATABASE']
We can then further update our application file to set up and teardown our database as required.
“`
<br><br>
<h1>app/<strong>init</strong>.py</h1>
<br><br>
“`
from config import Config
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
“`
<br><br>
db = SQLAlchemy()<br>“`
“`
<br><br>
def create<em>app():<br> …<br> # Allows you to get configurations<br> # from the config file with the exact environment<br> # that you require<br> app.config.from</em>object((get<em>environment</em>config()))<br>“`
db.init_app(app)
@app.before_first_request
def initialize_database():
""" Create all tables """
db.create_all()
...
@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
return app
“`
<br><br>
def get<em>environment</em>config():<br> if Config.ENV == "PRODUCTION":<br> return "config.ProductionConfig"<br> elif Config.ENV == "DEVELOPMENT":<br> return "config.DevelopmentConfig"<br>“`
With this, when we run the docker-compose up
, the application will start running and it will be connected with the MySQL service. However, there will not be any SQL actions being executed as we do not have any models set up yet.
GraphQL is a query language for APIs and runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
You can read more about GraphQL here.
$ pip install graphene Flask-GraphQL
Let’s do a breakdown of the packages that we just installed.
Flask-GraphQL is a wrapper layer that adds GraphQL support to the Flask application.
We’ll have to add a /graphql
route for the client to access the APIs. To do that, we can simply update our app/__init__.py
file.
“`
<br><br>
<h1>app/<strong>init</strong>.py</h1>
<br><br>
“`
“`
<br><br>
…<br>from flask_graphql import GraphQLView<br>“`
“`
<br><br>
def create_app():<br> …<br>“`
from app.schema import schema
app.add_url_rule(
'/graphql',
view_func=GraphQLView.as_view(
'graphql',
schema=schema,
graphiql=True # for having the GraphiQL interface
)
)
...
In this case, we have imported GraphQLView
from the Flask-GraphQL
module and have used it to implement our /graphql
route.
Next, we will be using the Graphene-Python package. Graphene is an open source library that allows Developers to build simple yet extendable APIs with GraphQL in Python.
If you have noticed from our latest app/__init__.py
file, we are actually missing an app/schema.py
file for our Flask application to work. A GraphQL schema is at the core of any GraphQL server implementation and describes the functionality available to the clients which connect to it.
In this case, we shall create a simple schema so that our application will be able to run without any error.
“`
<br><br>
<h1>app/schema.py</h1>
<br><br>
“`
from graphene import ObjectType, String
“`
class ExampleQuery(ObjectType):
hello = String()
<br><br>
<pre><code>def resolve_hello(self, info):<br> return "Hello"<br></code></pre>
<br><br>
“`
class RootQuery(ExampleQuery, ObjectType):
pass
“`
<br><br>
schema = Schema(query=RootQuery)<br>“`
With this, try running your application. When you access http://localhost:5000/graphql , you should be able to see this page. If you are familiar with GraphQL, this page should be very familiar to you. It’s an interface that allows you to enter GraphQL queries.
At this point, do remember to do a pip freeze and update your package dependencies.
I will be adding on an additional tutorial that focuses on implementing Graphene APIs. We can actually add on a default User Authentication module in our starter- kit.
Developers normally code in multiple environments. It could be just a Development
and a Production
environment or more, depending on their project requirements and budget.
One of the most common uses of having multiple environments is to be able to connect to different environment databases. In this case, we have a database service setup that we can serve locally with our application, which we can then use for local development.
We can run our application on multiple environments by editing the docker-compose.yml
file and changing ENV: DEVELOPMENT
to ENV: PRODUCTION
.
Also, Flask by default supports Hot-Reloading
in development mode.
Personally, what I love the most on this project is that I’m able to start new projects with functionalities that I require almost instantaneously. If I were to require some feature that is unique to myself, I can easily implement into the main repository and create a feature
branch that I can fork from.
I hope this article will give you a better understanding and overview of how you can build your own starter-kits. You can easily reference this and apply it to other frameworks as well.
I have also added a Github repository for reference here . In this repository, I have added an additional authentication
feature so that you can get a clearer idea on how to work with Graphene.
Thanks for reading, feedback is very welcome!
I mainly write about Technology & Programming. Given the opportunity, I would love to engage with the readers. You can follow me on Twitter or LinkedIn .
This content was originally published here.