Deploying a Django project on AWS Lambda using Serverless (Part 4)
Adding React.JS client to a Django project and deploying on AWS Lambda using Serverless
As I promised in my previous blog post Deploying a Django project on AWS Lambda using Serverless (Part 3), I'd like to show you how to add a React.JS client to a Django project and deploy it with Django on AWS Lambda using Serverless.
BLUF
Django framework allows you to build a client using Django templates, but there are a lot of cases when this is not enough. Business requirements for the client-side of your application could require adding more complex logic on the client-side. In these cases, we will not be able to solve business problems without a JavaScript web framework (React.JS, Vue.JS, Angular, etc). I'd like to show you how to build a simple React.JS client and integrate it with a Django project using axios
library on the client-side and Django REST Framework on the server-side.
With this approach, I will build a React.JS client with a domain name of CloudFront distribution as a PUBLIC_URL and store it on AWS S3 bucket together with Django static files. Then, I add the built index.html
file with React.JS app to Django templates folder and deploy it with the Django project on AWS Lambda.
Getting started
I've already described how to create a Django project and deploy it on AWS Lambda using Serverless in my first blog post of this series. I will use that project to get started.
Let's go through this process step by step:
- Clone the Django repository I used in the first part of these series and go to the cloned repository:
git clone https://github.com/vadym-khodak/django-aws-lambda
cd django-aws-lambda
- Follow instructions from this blog post to run the Django server (install requirements, configure environment variables using
.env
, apply migrations, create a superuser, collect static, run the server).
If everything works, you are ready to start working on the client.
Add React.JS client
In this part, I'll show how to create a simple React.JS client and integrate it with our Django project, but I'm sure that you can easily use Vue.JS (I'm not familiar with Angular) as steps pretty much the same.
There are many options to create React.JS client. I'm using react-scripts
in this example.
- Install
react-scripts
usingnpm
(Node package manager)
npm install -g react-scripts
- Use react-scripts to initialize a React.JS client
npm init react-app client
- Check that React.JS client was built successfully
cd client
npm run start
It will open your browser on localhost
port 3000
and you will see a page like this:
Update Django project configuration
Let's update our Django configuration to render index.html
file with React.JS client:
- Add
CLIENT_DIR
environment variable to.env
file:
CLIENT_DIR="client/build"
- Update
django_aws_lambda/urls.py
file with the following code:
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', include('hello.urls')),
path('', TemplateView.as_view(template_name="index.html"), {'resource': ''}),
path('<path:resource>', TemplateView.as_view(template_name="index.html")),
]
- Update
STATICFILES_DIRS
indjango_aws_lambda/settings.py
STATICFILES_DIRS = [
str(ROOT_DIR / env('CLIENT_DIR', default='client/build')),
str(ROOT_DIR / 'static'),
]
Check that our Django project can run the React.JS client
- Build production React.JS client locally the following commands:
cd client
export PUBLIC_URL=static/
npm run build
cd ..
- Collect static files by running the following command:
python manage.py collectstatic --no-input
- Run this Django project locally (make sure that you have already applied migrations and created a superuser):
python manage.py runserver
- Go to your browser, open this URL http://localhost:8000 and you will see a page like this:
It looks the same as we saw before by running npm run start
in client
folder. There are just a couple of differences - now it is run on port 8000
and it is run by our Django web server.
Make the React.JS client talk to the Django server.
First, we need to create an API endpoint to return some data from the server to the client. The easiest way to build REST API in Django is using Django REST framework project.
- Install Django REST framework and add it to
requirements.txt
file
pip install djangorestframework
- Create a new Django app called
users
by running the following command:
python manage.py startapp users
- Update
users/views.py
file with the following code:
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(["GET"])
def get_current_user(request):
return Response(status=status.HTTP_200_OK, data={"username": request.user.username})
This is an endpoint that returns Response object with
username
(if the user is not authorized it will return an empty string asusername
)
- Update
users/urls.py
file with the following code:
from django.urls import path
from .views import get_current_user
app_name = "users"
urlpatterns = [
path("me/", view=get_current_user, name="get-current-user"),
]
- Update our Django project configuration
Update django_aws_lambda/urls.py
file with the following code:
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', include('hello.urls')),
path('api/users/', include('users.urls')),
path('', TemplateView.as_view(template_name="index.html"), {'resource': ''}),
path('<path:resource>', TemplateView.as_view(template_name="index.html")),
]
- Update
INSTALLED_APPS
indjango_aws_lambda/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'hello',
'rest_framework',
'users',
]
Modifying React.JS client to send requests to the Django server
- Install
axios
library:
cd client
npm install axios -S
-S
flag will addaxios
library topackage.json
file
- Update
client/src/App.js
file with the following code:
import { useEffect, useState } from 'react';
import axios from 'axios';
import logo from './logo.svg';
import './App.css';
function App() {
const loadUserDetails = async () => {
const response = await axios.get('/api/users/me/');
return response.data;
};
const [userData, setUserData] = useState(false);
useEffect(() => {
loadUserDetails().then((payload) => {
setUserData(payload);
});
}, []);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<h1>Hello, World!</h1>
<h2>I'm {(userData && userData.username) || 'Unknown User'}</h2>
</header>
</div>
);
}
export default App;
- Build production optimized client by running the following command:
export PUBLIC_URL=static/
npm run build
cd ..
- Collect static files by running the following command:
python manage.py collectstatic --no-input
- Run this Django project locally:
python manage.py runserver
Go to your browser, open this URL http://localhost:8000 and you will see a page like this:
But if you authorize using your superuser username and password you will see a page like this:
Deploying on AWS Lambda using Serverless
Prepare AWS infrastructure
I've already described how to prepare AWS infrastructure in my previous blog posts, so you can use one of the following approaches:
- Prepare AWS infrastructure manually as it is described in Deploying a Django project on AWS Lambda using Serverless (Part 3) blog post
- Prepare AWS infrastructure automatically using terraform as it is described in Deploying a Django project on AWS Lambda using Serverless (Part 2) blog post
Update Serverless configuration
Add client
folder to package.exclude
to exclude it from deployment
Update URL in client/src/App.js
file to be able to send requests to production server
const response = await axios.get('/production/api/users/me/');
Use Docker for deploying your Django project to AWS Lambda using Serverless
- Prepare Amazon Linux 2 docker image with all the necessary dependencies:
docker run -it -v $(pwd):/root/src/ -v /Users/<your-username>/.aws:/root/.aws amazonlinux:latest bash
# install the necessary Unix dependencies:
yum install sudo -y
sudo yum install -y gcc openssl-devel bzip2-devel libffi-devel wget tar sqlite-devel gcc-c++ make
# install node.js version 14:
curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash -
sudo yum install -y nodejs
# install Python 3.8.7:
cd /opt
sudo wget https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tgz
sudo tar xzf Python-3.8.7.tgz
cd Python-3.8.7
sudo ./configure --enable-optimizations
sudo make altinstall
sudo rm -f /opt/Python-3.8.7.tgz
# create python and pip aliases:
alias python='python3.8'
alias pip='pip3.8'
# update pip and setuptools:
pip install --upgrade pip setuptools
# install serverless:
npm install -g serverless
# move to project directory
cd /root/src/
# install requirements inside docker container:
pip install -r requirements.txt
# set the necessary environment variables
export DJANGO_SETTINGS_MODULE=django_react_aws_lambda.production
export AWS_ACCESS_KEY_ID=<your-aws-access-key-id>
export AWS_SECRET_ACCESS_KEY=<your-aws-secret-access-key>
# migrate database changes
python manage.py migrate
# create a superuser in the database
python manage.py createsuperuser
# build React.JS client for AWS Lambda
cd client
npm install
export PUBLIC_URL="https://<your-cloud-front-distribution>.cloudfront.net/static/"
npm run build
# copy `index.html` from `client/build` to `templates`
cp build/index.html ../templates/index.html
cd ..
# collect static files to AWS S3 bucket
python manage.py collectstatic --no-input
# install serverless packages from package.json
npm install
# deploy your Django project to AWS Lambda using Serverless
serverless deploy -s production
Now, your Django Project with React.JS client will be available at this URL:
https://<some-id>.execute-api.<your-aws-region>.amazonaws.com/production
Without authorized user:
With authorized user:
How it works
When you go to the Django project URL on your browser it will go to AWS API Gateway that will trigger AWS Lambda function with the Django WSGI server. The Django server will render index.html
with React.JS app, the browser will use the domain name of the CloudFront distribution to get React.JS client from the AWS S3 bucket.
Final Words
In this blog post, we saw how to add the React.JS client to the Django project and deploy them on AWS Lambda using Serverless. There is a link to the GitHub repository (GitLab copy) with the code shown in this blog post.
It is the final blog post in this series. I showed just one of many ways to deploy a Django project on AWS Lambda, prepare AWS infrastructure, and add a React.JS client. You can find many other ways to do the same, it is up to you which approach to use.
Don't forget to follow me on Twitter @vadim_khodak or on LinkedIn so you do not miss the next posts.