Docker Compose: Redis, Flask and ReactJS – Part 1

In this series, I want to show you how to set up and deploy web applications using Flask and Redis on the Backend, and ReactJS on the Frontend. Using Docker Compose, it will be very easy to start, debug and deploy this application. My goal here is not to create a very sophisticated application, but rather to show you how we can use Docker to simplify development and deployment. Therefore, our Flask API will contain only a single controller (to show, add and remove items from a todo-list) that will be used by a ReactJS client.

In this part, we will set up a simple Web API, using Flask as a REST framework and Redis as a data store.

Software

Docker
Python
pip package manager

Additionally, you should install the following packages using pip:

pip install Flask flask_restful flask-cors redisworks

Project structure

Our project will consist of three parts: One client and one server directory, as well as a docker-compose.yml file to tie them together. So, the overall project structure should look like this:
 

 

Configuring Redis

We will start by configuring redis (see my previous post for more details).
First, we have to setup a custom config file, so add the default redis.conf from http://download.redis.io/redis-stable/redis.conf to the root directory of the project.
To easily connect to a local Redis instance from Python, comment out the following line in the config:

bind 127.0.0.1

by adding a # in front of it.
Note that you should only use this configuration for development, for deployment, please consider the documentation to set up a secure and persistent instance of Redis.

Setting up Redis

At the core of our application, redis will be the first service we add to our docker-compose.yml file:

version: '2'
services:
  redis:
    image: redis
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - /redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"

Save your progress, open a terminal in the root directory and type:

docker-compose up

. After the image is downloaded and started, we should have a redis-instance ready to accept connections.

Setting up Flask

Now it’s time to set up the Flask API for our project. In the server directory, we will first add a requirements.txt file containing all the dependencies of the project:

Flask
flask_restful
flask-cors
redisworks

With that, creating our API is a simple as adding an app.py file to the server directory and initializing Flask inside it:

#!flask/bin/python
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

I added the default CORS headers to make sure we can access the API easily from our React app later. Again, you should use a more secure configuration for production deployment!

Adding a todo-list controller

Now we are ready to add the first controller to our app, inside the server directory, add the following todo.py file:

from flask import Blueprint, request, jsonify
from redisworks import Root


root = Root
root.todo_list = {"item1": "This is a nice day", "item2": "Python is awesome"}
todo_api = Blueprint('todo', 'todo', url_prefix='/todos')


@todo_api.route('/', methods=['GET'])
def api_list():
    items = root.todo_list
    return jsonify(items)


@todo_api.route('/<item_id>', methods=['GET', 'DELETE', 'PUT'])
def api_item(item_id):
    if request.method == 'GET':
        return get_item(item_id)
    elif request.method == 'PUT':
        return put_item(item_id)
    else:
        delete_item(item_id)


def get_item(item_id):
    return root.todo_list[item_id]


def put_item(item_id):
    item = request.data
    root.todo_list[item_id] = item.decode('utf-8')
    return item, 201


def delete_item(item_id):
    root.todo_list.__delitem__(item_id)

In the first three lines after the import statements, we set up a redis-client using redisworks and initialize a small todo-list in it. I also added a blueprint for our the todo-API which will simplify accessing and configuring it later. The next lines contain the methods that will be called when we send requests to our API. I added a api_list method to return the entire list as a simple json document as well as methods to get/add/change/delete a single item.
Again, this is not a very sophisticated example, but it will do fine for our needs.

Making the todo-API accessible

Since we used the Blueprint class to configure our todo-API, configuring Flask to use it is very easy. Simply import the todo_api defined above in the app.py file and register it:

#!flask/bin/python
from flask import Flask
from todo import todo_api
from flask_cors import CORS

app = Flask(__name__)
app.register_blueprint(todo_api)
CORS(app)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

That’s it! With Docker Compose running our redis instance, you should now be able to run and debug the API and send requests to it. Go to http://localhost:5000/todos/ and you should see the two todo-items we initialized our todo-list with:

 

 

Dockerizing our todo-API

To run this API from Docker, we must first add a Dockerfile to the server directory. I used the basic Python+Flask container from http://containertutorials.com/docker-compose/flask-simple-app.html. It will install Python and all dependencies from the requirements.txt file and run our app.py file to start the API.
To start this Docker-image with Docker Compose, simply add the following service to the docker-compose.yml file:

  web:
    build: ./server
    working_dir: /var/www/app
    ports:
     - "5000:5000"
    volumes:
     - ./server:/var/www/app:rw
    depends_on:
     - redis

By pointing build to the server directory, Docker Compose will detect and run the Dockerfile we just created. I added a dependency on the redis-service to make sure our API doesn’t receive requests before Redis is started.

Conclusion

That’s it for part 1, in the next part, we will create a simple ReactJS app to use the API we just created and connect it with our docker-compose.yml file.
If you have any questions, problems or feedback, please let me know.