Image for post
Image for post
Photo by Kevin Ku on Unsplash

How to Deploy Django REST Framework and React-Redux application with Docker

Okan Çakmak
Jun 29 · 9 min read

Sometimes It’s really overwhelming when you think about how many things from different expertise areas(front-end, back-end, devops…) you should know even for running a simplest website from scratch. In this tutorial, I’ll create very simple DRF endpoint and use it with a simple(but very easy to extend) Single Page React Application. Each service will live it’s own docker container and I’ll use docker-compose to manage all the deployment flow. I believe you can use this full-stack project as a base to build more complex applications in case needed.

This tutorial will not fully cover each topic. You are assumed to have basic of DRF and React development information. Aim of the tutorial is to show you how each component works with the other and finally give you a production-ready deployed full-stack application.

In this tutorial, you’ll learn:

  • How to implement a simple CRUD operation with DRF.
  • How to create a single-page react app and connect it with the API.
  • How to deploy a frontend app and a backend app separately.
  • How to use docker and docker-compose basically.

In this tutorial, you’ll use:

  • Django REST Framework
  • React & Redux
  • Docker
  • NGINX

In this project I’ll build a micro-blog application. Any user can write a piece of entry (formed by a title and a content), and also any user can read what is written so far.

When the project is deployed:

1.We’ll have a backend docker container which runs the API.

2. We’ll have an frontend docker container that builds the javascript code and passes this frontend app bundle to NGINX.

3.We’ll have an webserver docker container that runs NGINX which will serve the static frontend app and route API calls to the backend docker.

All of the deployment steps will be handled by docker-compose with only one simple command!

You can check the finished project in github.

There will be total 3 parts in this tutorial:

  1. Setting up Backend
  2. Setting up Frontend
  3. Dockerize and Deploy!

Part 1: Setting up Backend

At this part I’ll build a very simple CRUD endpoint. Django REST Framework makes our job very easy. With built-in ModelViewSets, It just takes a few lines of code to create a fully working .

Again I won’t be covering details of DRF in this post. But if you are a beginner and interested there are lots of tutorials you can find online.

Let’s start with setting initial project layout. Main project folder will be named after “blog” . Inside “blog” folder, there will be a “backend” and a “frontend” folder also.

# create directory
$ mkdir blog
$ cd blog
# create virtualenv named env and activate it
$ python3 -m venv env
$ source env/bin/activate
# install packages
$ pip install django djangorestframework django-cors-headers gunicorn
# create project named blog_backend
$ django-admin startproject blog_backend
# create an app named 'blog' within project 'blog_backend'
$ python manage.py startapp blog
# let's check if everything is OK so far.
# First migratation, then start the development server.
$ python manage.py migrate && python manage.py runserver

If everything is OK , then you’ll see:

System check identified no issues (0 silenced).
June 23, 2020 - 11:28:23
Django version 3.0.7, using settings 'blog_backend.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

So far so good!

Before moving on with coding, let’s create a requirements.txt file to keep a list of packages used in this project(We’ll need this for deployment later at chapter 3)

$ pip freeze > requirements.txt

So let’s start coding step by step. Generally speaking, in DRF, request travels through

urls > views >serializers > models

I’ll start coding in following order, to make it more understandable

  1. Create Model
  2. Create View
  3. Serializers
  4. URL Configuration
  5. Django Settings Configuration
  6. Migration
  7. Run!
  1. Create Model

In Django, models are the files where we define DB tables in form of Python Classes. Django creates real SQL tables according to this classes. Let’s create a very basic model named ‘Post’ to store blog entries of users. ‘Post’ model will store title,content and timestamps for blog entries.

blog/blog_backend/blog/models.py

2. Create Views

Django views are the files where we put our logic. I’ll be using DRF’s built in ModelViewSet for simplicity’s sake. ViewSets are great tools if you are very standard CRUD operation on a model. Again I’d like to remind you, I won’t be telling you much about DRF to keep this article as direct and dense as possible. You can find great resources about Django and DRF online.

3. Serializers

Again, I’ll be using built in ModelSerializer from DRF. Basically it has following operations under the hood:

  • Validates user input
  • Converts JSON to Django ORM object (Deserialize) for POST,PUT requests
  • Converts Django ORM object to JSON (Serialize) for GET request
blog/blog_backend/blog/serializers.py

4. URL Configuration

I want this ‘Post’ resource is accessible via

/api/post

In order to do this, I’ll set both project level URL and an app level URL configuration(this makes the URL configuration easy to extend in case needed). Django first checks the project’s urls.py file, I’ll add an URL named ‘api’ and redirect this to the blog app’s urls.py file.

blog/blog_backend/blog_backend/urls.py

Create an urls.py file and redirect incoming request to the view.

blog/blog_backend/blog/urls.py

5. Django Settings Configuration

Both ‘rest_framework’ and ‘blog’ must be defined in INSTALLED_APPS in order to make Django use them.

6. Migration

Django keeps track of the changes in the Models via Migrations. Each change in the model.py file produces an migration file in the migration directory of the app. In this case a new model created.

# makemigrations command triggers django to detech model changes.
$ python manage.py makemigrations
# migrate command applies changes into the database
$ python manage.py migrate

7. Run!

If everything is OK so far, Django will reward you with

http://127.0.0.1:8000/api/posts

endpoint which answers POST,GET,DELETE requests

$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
June 23, 2020 - 12:03:52
Django version 3.0.7, using settings 'blog_backend.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Part 2: Setting up Frontend

Let’s put python aside for now and continue with frontend part of the project . The frontend part of the project will allow users to:

  • Browse all blog entries,
  • Create a new blog entry,
  • Delete an existing entry

I’ll use

  • React with Redux as frontend framework.
  • ant.design for the UI components. If you don’t like writing CSS like me, it’s such cool way to deal with visuals.

If you’ve ever started to a frontend app from scratch, you know that there are lots of tiny details and configurations before start coding. But, luckily, we have a tool to skip that part : create-react-app!. Create react app gives us huge head-start, deals with all tiny details for us. Let’s start with project layout and project initialization:

# Create initial project
# in the 'blog' directory
$ npx create-react-app blog_frontend
$ cd blog_frontend
# run development server
$ yarn start

This command will start development server at localhost:3000.

Image for post
Image for post

So far so good! At this point let’s install packages we’ll use. Briefly,

  • antd: this is for UI components
  • redux: well, this is obvious
  • axios: for making http requests
  • react-redux: this binds react and redux together
  • redux-thunk:this is a middleware for dispatch async events
  • redux-logger: this is a middleware eases debugging.
# Create initial project
# in the 'blog' directory
$ yarn add antd redux axios react-redux redux-thunk redux-logger

Let’s get into coding. create-react-app generates bunch of codes for us. I’ll also create two folders inside ‘src’ folder named Containers and Redux to separate React components and Redux related stuff.

Inside Redux folder, there will be actions.js, reducers.js, store.js and types.js

I’ll start with setting up the redux store:

src/Redux/store.js

After that, the action types I’ll dispatch :

src/Redux/types.js

There will be methods for getting all the posts, creating a post, and deleting a post. Please notice I’ve used env.REACT_APP_HOST_IP_ADDRESS here instead of hard-coded API URL. This will be helping us in the deployment stage.

src/Redux/actions.js

Lastly for Redux part, I’ll create a reducer to shape the Redux store. Notice this reducer holds the information of all post entries (posts) and a flag to understand if any backend call is active(loading).

src/Redux/reducers.js

Done with Redux. Lets write the component real quick.

I’ll crate 3 pages inside Containers folder.

  • a page for displaying all posts, (ListPosts.js)
  • a page for creating a post. (CreateNewPost.js)
  • a main page allows user to select between ListPosts and CreateNewPost pages (Blog.js)
src/Components/Blog.js
src/Containers/CreateNewPost.js
src/Containers/ListPosts.js

I’ll also move App.js into Container folder to keep related parts together and wrap the blog project with Provider component to integrate store to the react components.

src/Containers/App.js

And because index.js have to point App component, import statement should be changed.

src/index.js

OK, let’s give it a go again, this time, The app will make API calls to the backend.

# Remember API address is set via env variable so
$ REACT_APP_HOST_IP_ADDRESS=http://localhost:8000 yarn start
Image for post
Image for post
CreateNewPost page
Image for post
Image for post
ListPosts page

Part 3: Dockerize and Deploy!

Now that frontend and backend is ready separately. Docker will provide us a elegant way to deploy the application any server with a minimal effort. I’ll split the application into 3 services:

  • Backend
  • Frontend
  • Webserver (NGINX)

I’ll use docker-compose to build and run those 3 docker containers. I’ll write Dockerfile for each backend and frontend services. At the end I’ll run Dockerfiles with docker-compose. With this approach, Each service will completely isolated from one another. All related codes, configurations and Dockerfiles will be in their related folder. What’s nice about this is, you can deploy frontend and backend to different servers with minimal changes if it’s required.

Backend Service

Let’s start with dockerizing backend service. There are some settings must be made in Django’s settings.py file to make it production ready.

Next I’ll add Dockerfile in blog/blog_backend:

blog/blog_backend/Dockerfile

Frontend Service

Second, another Dockerfile for frontend to blog/blog_frontend

blog/blog_frontend/Dockerfile

Third, I’ll create a folder named webserver in main ‘blog’ directory. This folder will keep NGINX server configuration.

# in the blog directory
$ mkdir webserver

and create configuration for NGINX

blog/webserver/nginx-proxy.conf

Lastly, a docker-compose file will run them,

blog/docker-compose.yml

Did you see how frontend and NGINX service shares a volume named “build_folder” ? With that trick, frontend container builds and creates static bundle into “build_folder” volume, after that, NGINX finds this static bundle according to it’s conf and serves it.

Briefly this docker-compose file will do:

backend service:

  • set args to be used as environment variable,
  • Build backend service with Dockerfile inside blog_backend folder. Run gunicorn and listen 8000 port

frontend service:

  • Set environment variable for backend calls via API_URL parameter.(Please notice how environment variable set in docker-compose and it’s passed to built app via Dockerfile)
  • Build React code and share this bundle with NGINX service via volumes functionality.

webserver service:

  • Copy the configuration file in host machine to container.
  • Create a webserver service with latest NGINX image.

With this docker-compose file, all you need to do for deployment is,

to fill DJANGO_ALLOWED_HOSTS and API_URL , DJANGO_SECRET_KEY and API_URL parameters according to your host and requirements. Example values are:

# You can use directly api for the allowed hosts variable, because it's defined in nginx's conf.DJANGO_ALLOWED_HOSTS: ec2-3-122...amazonaws.com, api
DJANGO_SECRET_KEY:'n%osd8*8=tslpj68s...$sao*_^=!z=*ck@0w'
DJANGO_CORS_ORIGIN_WHITELIST: http://ec2-3-122-...amazonaws.com
API_URL: http://ec2-3-122...amazonaws.com

You might need to set CORS whitelist variable if you are going to use different hosts for your backend and frontend service.

After everything is set, run

$ docker-compose up

If everything is correct so far, you’ll see services go up one by one. And if you check

[ec2-user@ip-172-31-21-251 ~]$ docker container ls

Then you will see there are 2 active containers named blog_nginx_1 and blog_backend_1. As I stated before, frontend docker builds the code and passes it to webserver. After this, it shuts down.

Thank you for reading! I hope you’ve find something useful for yourself in this article. Please share and use it if you think someone else might be interested to read this!

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store