DEV Community

Cover image for Dockerize a Django, React, and Postgres application with docker and docker-compose | by Anjal Bam
AnjalBam
AnjalBam

Posted on

Dockerize a Django, React, and Postgres application with docker and docker-compose | by Anjal Bam

Introduction

In the battlefield of modern web development, Django and React are both very great warriors that have been fighting battles for a very long time. The addition of our Knight PostgreSQL to the battle makes our tech stack unbeatable in the modern war.

Containerizing an application is the packaging of the software with its own OS, libraries, and dependencies required to run the software code and create a single lightweight executable - which is called a container. It runs consistently on any infrastructure. We will be using Docker and docker-compose to package our application.

Prerequisites

This tutorial assumes a good grasp of React and Django. This also assumes familiarity with docker and containerization in general.

Make sure docker and docker-compose are installed on your system.

Download docker from here

Getting Started

Without further ado, Let's start by creating an empty folder. Give it whatever name you like I am going to call mine django-react-docker. This will be our workspace where our React and Django code will exist.

Setting up Django application

Create a virtual environment

First things first, create a virtual environment with virtualenv. (If not installed install with pip install virtualenv.) Create a new virtual environment with the following command in terminal.

virtualenv venv  # Specify python version if multiple installed
Enter fullscreen mode Exit fullscreen mode

this will create a new folder venv. Now activate the environment with following command:
Windows:

.\venv\Scripts\activate
Enter fullscreen mode Exit fullscreen mode

Linux/Unix

source ./venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Installing dependencies

Install the required dependencies to run the Django application with the following command

pip install django djangorestframework django-cors-headers psycopg2-binary
Enter fullscreen mode Exit fullscreen mode

These are the minimal dependencies we will need for our application to function.

Create a requirements file

The requirements file will keep a list of dependencies used in our application. Use the following command in the backend folder to create a requirements.txt file.

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

Create a Django project

Let us create a django project by using the following command:

django-admin startproject backend
Enter fullscreen mode Exit fullscreen mode

Note: The backend is the name of the Django project name it whatever you want.

You'll see a project named backend in the root of your workspace.

Start the project

Now, start the project by executing the following commands:

cd backend
Enter fullscreen mode Exit fullscreen mode
python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Make sure the virtual environment is active.

Visit http://localhost:8000to see the default Django page.

Setting up the React Application

Creating the React application

First, make sure you are in the root directory. And run the following command:

npx create-react-app frontend
Enter fullscreen mode Exit fullscreen mode

Make sure the Node.js is installed. We are using 16.13.0. Or you can download it from here.

Spin up the development server

To start the development server use the following commands:

cd frontend
Enter fullscreen mode Exit fullscreen mode
npm start  # or yarn start if  you're using yarn
Enter fullscreen mode Exit fullscreen mode

You must see the default react page on http://localhost:3000/.

Let's start Containerizing

Containerize the Django application

First, create a file .dockerignore. and then add the following content to the file.

venv
env
.env
Dockerfile
Enter fullscreen mode Exit fullscreen mode

Next, Create a file named Dockerfile. This is the file that will contain our docker configurations for the backend. Add the following content to the file.

FROM python:3.8-alpine

ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1

WORKDIR /app/backend

COPY requirements.txt /app/backend/

# Build psycopg2-binary from source -- add required required dependencies
RUN apk add --virtual .build-deps --no-cache postgresql-dev gcc python3-dev musl-dev && \
        pip install --no-cache-dir -r requirements.txt && \
        apk --purge del .build-deps

COPY . /app/backend/

CMD [ "python", "manage.py", "runserver", "0.0.0.0:8000" ]
Enter fullscreen mode Exit fullscreen mode

In the above code,

  • We start with a base image set variables PYTHONUNBUFFERED and PYTHONDONTWRITEBYTECODE to 1 for logging and not creation of .pyc files respectively.
  • Set the working directory inside the container to /app/backend/ for the backend.
  • Copy the requirements file to the working directory and install the requirements.
  • Install required dependencies for building the psycopg2-binary from source
  • Copy the content of our backend to the docker container
  • The starting command for our container

Note: The venv and other mentioned files and folders in .dockerignore file are not copied.

Containerize the React application

Create a .dockerignore file in the /frontend/ directory, and add the following code:

node_modules
npm-debug.log
Dockerfile
yarn-error.log
Enter fullscreen mode Exit fullscreen mode

Also, create a file named Dockerfile and place the following content in it.

FROM node:16-alpine

WORKDIR /app/frontend/

COPY package*.json /app/frontend/
RUN npm install

COPY . /app/frontend/

CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

The above code will do the following:

  • Starting with a base image, it will set the working directory to /app/frontend where our code shall reside.
  • Copy package.json and package-lock.json files to working directory.
  • Install all the dependencies.
  • Copy our code content to the working directory.
  • Set server starting command withCMD

Packaging our applications with docker-compose

For this, create a docker-compose.yml file in the root folder, inside django-react-docker, which will be the configurations for our application.
Place the following code content in the docker-compose.yml file.

version: '3.9'


services:
  db:
    image: postgres:14-alpine
    ports:
      - '5432:5432'

    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres

    volumes:
      - ./data/db:/var/lib/postgresql/data/

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile

    ports:
      - '3000:3000'

    volumes:
      - ./frontend:/app/frontend

    depends_on:
      - backend

  backend: 
    build: 
      context: ./backend
      dockerfile: Dockerfile

    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres

    ports:
      - '8000:8000'

    volumes:
      - ./backend:/app/backend

    depends_on:
      - db
Enter fullscreen mode Exit fullscreen mode

So, here's what the above code does,

  • We start by mentioning the version of docker-compose.
  • And create three services: Database (db), Frontend React (frontend), Backend Django (backend)
  • For db we point it to the official postgres image, set environment variables, set volumes, and expose ports 5432:5432
  • Also for frontend and backend we provide the build context i.e. the dockerfile to use for building the image expose ports 8000:8000 for backend and 3000:3000 for frontend.
  • Set volumes to map ./frontend and ./backend inside the container.
  • The depends_on property will ensure that the db is running before starting the backend and backend is running before starting the frontend.

Building our containers

To build our application, use the following command from the project root:

docker-compose build
Enter fullscreen mode Exit fullscreen mode

After the successful command execution, if you list out all the images with following command:

docker images

# Or
docker image ls
Enter fullscreen mode Exit fullscreen mode

We must see the images named django-react-docker_frontend and django-react-docker_backend. This means our build was successful.

Running the containers

The running of our containers is as simple as running the following command:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

This command will spin up three containers: one for backend, one for frontend and one for our database and create a network (more on networks in docker) and attach containers to the netork.

After this, the servers are accessible at the ports 3000 for frontend, 8000 for backend and 5432 for db. Visit here and you must see the react default page.
and here for the default 'django is running' page.

Stop the containers

To stop the containers we can just press Ctrl+C or we can use the following command:

docker-compose down
Enter fullscreen mode Exit fullscreen mode

Note: The docker-compose down command will remove the default network created and stop and remove the containers as well.

Check if our application works

So, now we have our containers running, we're ready to develop in our containers.

To check if our configuration works, we will try to connect to the database, build a very simple API and display some data in the frontend.

Build the API first

Step 1: Change the database

First, open the folder django-react-docker in a code editor. Edit and change the settings.py file.

  • Change the database driver and configurations, find the DATABASES variable in the file and replace it with following content.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': '5432',
    }
}
Enter fullscreen mode Exit fullscreen mode

The above code snippet sets the django backend to use PostgreSQL as the database and define different configurations required to connect the database.

Note the NAME, USER and PASSWORD are the same values from the env we provided in the compose docker-compose.yml file and host is our service db and port accordingly.

Step 2: Set up CORS

By default cross application data-sharing is prohibited by the CORS policy. We need to configure our backend to allow sharing the data.

For this we use our django-cors-headers package we installed before. Change the following sections in the settings.py file.

INSTALLED_APPS = [
    # .... Other code
    'corsheaders',  # Add this
]
Enter fullscreen mode Exit fullscreen mode
MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    ... # Remaining code
]

CORS_ALLOW_ALL_ORIGINS=True # Add this line too
Enter fullscreen mode Exit fullscreen mode

The above code allows all the sites to access the data from our server but in production we will more likely use the CORS_ALLOWED_ORIGINS variable to set up a list of allowed origins.

More on django-cors-headers here.

Create an API

For the sake of this tutorial we will not be following best practices for the API building but create a rather simple API for testing our connection.

First, add rest_framework to our installed apps in settings.py.

INSTALLED_APPS = [
    # .... Other code
    'rest_framework',  # Add this
]
Enter fullscreen mode Exit fullscreen mode

Create a file views.py with path as django-react-docker/backend/backend/views.py and write the following code:

# django-react-docker/backend/backend/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['GET'])
def send_some_data(request):
    return Response({
        "data": "Hello from django backend"
    })
Enter fullscreen mode Exit fullscreen mode

Also in the urls.py file.

# django-react-docker/backend/backend/urls.py
...
from . import views

urlpatterns = [
    # ... other code
    path('test/', views.send_some_data), # Add this
]
Enter fullscreen mode Exit fullscreen mode

Now visit the http://localhost:8000/test/ to see the response {data: 'Hello from django backend'}

Consume the API from frontend

In our frontend directory, open folder src/App.js and replace the whole content with the following code snippet and save the file.

import logo from './logo.svg';
import './App.css';
import {useState, useEffect} from 'react';

function App() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('http://localhost:8000/test/')
      .then(res => res.json())
      .then(data => setData(data.data));
  })

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1>An Awesome Blog </h1>
        <h3>On Django, React, Postgres, and Docker </h3>

        <p>{data}</p>
      </header>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now if you visit http://localhost:3000/ you must see the following page:

Image description

The above code snippet in the frontend sends a request to the backend with URL http://localhost:8000/test/ and sets it to a state data which is rendered in the UI later.

This means our frontend can successfully connect to the backend and fetch API data as well.

Conclusion

This is it!!!

Now we can start developing the application.

With this tutorial, we learned how to containerize and run an application with Django as backend, React as frontend, and PostgreSQL as our database.

This was originally posted here on my blog

Top comments (12)

Collapse
 
akhilnasim1123 profile image
Akhil Nasim • Edited

getting error 'docker-compose build'

docker-compose : The term 'docker-compose' is not recognized as the name of a cmdlet,
function, script file, or operable program. Check the spelling of the name, or if a

path was included, verify that the path is correct and try again.

Collapse
 
anjalbam profile image
AnjalBam

It seems like you've installed docker desktop. The latest version of docker compose has changed their command from 'docker-compose' to 'docker compose'.

So maybe you can try "docker compose build" instead.

Collapse
 
mayhrem profile image
Rene Francisco Cruz González

I really appreciate your post, It was very helpful and the good documentation was also good. Thanks for this contribution!

Collapse
 
anjalbam profile image
AnjalBam

I am glad it helped. Happy coding!

Collapse
 
johannquispe profile image
JohannQuispe

gracias mano, esto como funcionaria en produccion! perdona mi ignorancia

Collapse
 
jliucoder profile image
jLiucoder

Is it possible that we can have a toturial talking about how to use the pgadmin4 and connect with the the database, I have been trying different solutions but couldn't get it working, thanks so much for this!

Collapse
 
jliucoder profile image
jLiucoder

when i try to migrate the database it just shows me "could not translate host name "db" to address: nodename nor servname provided, or not known", could you tell me the possible solutions to fix this? Thanks so much!

Collapse
 
anjalbam profile image
AnjalBam

This is because you tried migrating from the normal shell, instead you should try updating it inside the docker container, you can do it with command "docker compose exec backend python manage.py migrate/makemigrations".

Thread Thread
 
jliucoder profile image
jLiucoder

Thanks so much for the help! If i am changing the dababase from postgres to mySQL, all i need to do it changing the database in the settings file right?

Thanks again, you def deserve more views and likes,

Thread Thread
 
anjalbam profile image
AnjalBam

Yes... you must write separate configurations for the MySQL Database server. But in django application only settings file changing is sufficient.

Glad to help! I really appreciate you saying those kind words.

Collapse
 
akhilnasim1123 profile image
Akhil Nasim

i have 5000 port for express route to call api to backend. and i have 8001 port for chatgpt integration. running code of 8001 is 'python app.py'. how can i add these two engine into docker with my frontend(3000) and backend(8000)

Collapse
 
johannquispe profile image
JohannQuispe

Muchisimas gracias me fue de mucha ayuda, pero disculpen mi ignorancia,!
como puedo llevar esto a produccion?!