Django Rest Framework with ReactJS and Swagger UI implementation: Part1
This will be a 2 part series tutorial, where, in the first part we will learn how to build the rest APIs using Django Rest Framework (DRF) and in the second part we will create a simple ReactJS web app to consume the DRF APIs.
Let's get started with part 1
Just follow the tutorial step by step, and you will be able to achieve your goal to create your first API using DRF and swagger UI.
The reason I am writing this tutorial blog is that I found it difficult to get an end-to-end solution to the problem I faced while learning and implementing DRF at work. Here In this example-based tutorial, you will implement a simple user login/signup using DRF and you will document it using Swagger and will use JWT authentication.
PREREQUISITES
- Python installed
- Python virtual environment preferred
- Text editor of choice ( Pycharm, VS code, etc)
- should know what’s an API
- Django Basics
Steps:
- Activate the Python Virtual Environment and run the following commands
pip install Django
pip install djangorestframework
pip install drf-yasg
pip install django-rest-swagger
pip install django-cors-headers
pip install PyJWT
2. Create a new project, I used the name drf_tutorial as my project name.
mkdir drf_tutorial
cd drf_tutorial
3. write the following command to create a Django project inside the drf_tutorial folder with the name “tutorial”
django-admin startproject tutorial
your folder structure till now must look like this:-
4. now let's create a new app and name it “api”, you can name it whatever you wish to. write the following commands.
cd tutorial
django-admin startapp api
by now your folder structure must look like this:-
5. let’s sync our database for the first time.
python manage.py migrate
6. let’s create a superuser using the following command
python manage.py createsuperuser
give a username, email, and password
Now I know we have a default user model in Django, but here we will create our own user model for the sake of learning to create custom models.
7. let's register our app named “api” in the INSTALLED_APPS inside settings.py but before that, we also need to tell Django that we are creating rest APIs, we do this by specifying rest_framework inside INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api',
]
8. Now in the models.py file write the following lines of code
from django.db import models
# Create your models here.
class User(models.Model):
userid = models.IntegerField(primary_key=True)
email = models.CharField(max_length=100, unique=True)
fname = models.CharField(max_length=100)
password = models.CharField(max_length=100)
username = models.CharField(max_length=1000)
9. next we need to register this model in our admin.py file inside our api, open the admin.py file, and write the following code
from django.contrib import admin
# Register your models here.
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ['userid', 'email', 'fname', 'password', 'username']
10. run the following two commands to make migrations of the changes done till now.
python manage.py makemigrations
python manage.py migrate
11. now run the server using the command
python manage.py runserver
open the server on localhost, you'll see something like this on your browser
type in the URL http://127.0.0.1:8000/admin and you'll be redirected to the following page, where you can enter the username and password we created and see the User table under the api section, which we will use to create and authenticate a user.
12. Now let's create the serializers, but wait a minute what are serializers?
Serializers are used to convert the data sent in a HTTP request to a Django object and a Django object to a valid response data. It looks a lot like a Django Form, but it is also concerned in defining how the data will be returned to the user.
As per the DRF documentation , Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into
JSON
,XML
or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
Create a serializers.py file inside the api folder and write the following code inside it.
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'fname', 'password', 'username']
class LoginDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['userid', 'email', 'fname', 'username']
class LoginSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email','password']
13. now let's create the views, in views.py file inside the api, write the following code:
As per Django Documentation, A view function is a Python function that takes a Web request and returns a Web response.
REST framework provides an
APIView
class, which subclasses Django'sView
class.
APIView
classes are different from regularView
classes in the following ways:Requests passed to the handler methods will be REST framework’s
Request
instances, not Django'sHttpRequest
instances.Handler methods may return REST framework’s
Response
, instead of Django'sHttpResponse
. The view will manage content negotiation and setting the correct renderer on the response.Any
APIException
exceptions will be caught and mediated into appropriate responses.Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.
from drf_yasg.utils import swagger_auto_schema
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer, LoginDetailsSerializer, LoginSerializer
from rest_framework import status, permissions
from rest_framework.views import APIView
import datetime
from django.conf import settings
import jwt
# Create your views here.
class SignupAPI(APIView):
@swagger_auto_schema(request_body=UserSerializer)
def post(self, request):
data = request.data
serializer = UserSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response({'msg': 'Data Created'}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginApi(APIView):
@swagger_auto_schema(request_body=LoginSerializer)
def post(self, request):
email = request.data.get('email')
password = request.data.get('password')
user = User.objects.filter(email=email, password=password).first()
if user:
user = LoginDetailsSerializer(user)
auth_token = jwt.encode({'email': user.data.get('email'), 'userid': user.data.get('userid'),
'exp': datetime.datetime.timestamp((datetime.datetime.now() + datetime.timedelta(days=1, hours=3)))},
settings.SECRET_KEY, 'HS256')
data = {
'user': user.data, 'token': auth_token
}
return Response({'data':data, 'issuccess':True}, status=status.HTTP_200_OK)
return Response({"message": "Invalid Credentials"}, status=status.HTTP_401_UNAUTHORIZED)class UserDetails(APIView):
permission_classes = [permissions.IsAuthenticated, ]
def get(self, request, pk=None, format=None):
userid = pk
if userid is not None:
stu = User.objects.filter(userid=userid)
serializer = LoginDetailsSerializer(stu, many=True)
return Response({'data': serializer.data}, status=status.HTTP_200_OK)
stu = User.objects.filter(userid=request.user.userid)
serializer = LoginDetailsSerializer(stu, many=True)
x = [dict(i) for i in serializer.data]
return Response({'data': x}, status=status.HTTP_200_OK)
@swagger_auto_schema defines() what defines the schema we will use in the swagger, it takes the serializer as the argument and forms the schema according to the serializer.
14. now let's make some changes in the settings.py file so that we are able to accommodate swagger UI in our project. we will also have to protect our API from CORS error while accessing from ReactJs web app.
In the INSTALLED_APPS list add drf_yasg and corsheaders
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api',
'drf_yasg',
'corsheaders',
]
In the MIDDLEWARE list add ‘corsheaders.middleware.CorsMiddleware’
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Add a dictionary with the name SWAGGER_SETTINGS in the settings.py file which will allow us to pass the JWT token inside the Swagger UI for authentication
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
"Auth Token eg [Bearer (JWT)]": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}
Now add two more dictionaries in settings.py which are essential for Swagger JWT Authentication and to specify allowed origins without CORS error
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.backends.JWTAuthentication',
),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
}
CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://sub.example.com",
"http://localhost:8080",
"http://127.0.0.1:9000",
"http://127.0.0.1:3000",
"http://localhost:3000"
]
15. now replace the code in the urls.py file inside the tutorial folder with the following code.
from django.contrib import admin
from django.urls import path
from api import views
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions
schema_view = get_schema_view(
openapi.Info(
title="DRF Tutorial API",
default_version='v1',
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
path('admin/', admin.site.urls),
path('signupapi/', views.SignupAPI.as_view()),
path('loginapi/', views.LoginApi.as_view()),
path('userdetails/', views.UserDetails.as_view()),
path('userdetails/<int:pk>/', views.UserDetails.as_view()),
]
We have talked so much about the JWT token, now let's finally implement it.
16. Create a python file with the name backends.py inside the api folder and write the following code in it.
import jwt
from rest_framework import authentication, exceptions
from django.conf import settings
from .serializers import LoginDetailsSerializer
from .models import User
def is_Authenticated():
return True
class JWTAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
auth_data = authentication.get_authorization_header(request)
if not auth_data:
return None
prefix, token = auth_data.decode('utf-8').split(' ')
try:
payload = jwt.decode(token, settings.SECRET_KEY, 'HS256')
User.add_to_class("is_authenticated", is_Authenticated)
user = User.objects.filter(email=payload['email']).first()
return user, token
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed(
'Your token is invalid,login')
except jwt.ExpiredSignatureError as identifier:
raise exceptions.AuthenticationFailed(
'Your token is expired,login')
return super().authenticate(request)
Congratulations !!! You have created your first API using DRF using Swagger UI and JWT authentication.
Now let's run and test our API
python manage.py runserver
You will see swagger UI on your localhost
Github Link: https://github.com/shorya1996/drf-reactPart1
- Signup testing
2. Login testing
3. Testing JWT and getting user details
If no token is provided
providing a wrong token
Providing the correct token
The second part of the tutorial can be found here :
Also If you interested In building APIs using flask restplus, check out this tutorial of mine:
https://sharmashorya1996.medium.com/flask-restplus-swagger-token-authentication-blueprints-84272b6bcef1