DEV Community

Cover image for Django Microservices, A Practical Guide
Saif Ullah Usmani
Saif Ullah Usmani

Posted on

Django Microservices, A Practical Guide

A Django app is monolith, not a microservice, by default.
A microservice is a separately deployed Django project with its own auth boundary.

Below is the simplest, correct way to do this using JWT shared between two Django services.


Service 1, Auth Service

This is a standalone Django project whose only job is to issue JWTs.

settings.py

DJANGO_SECRET_KEY = "same-secret-between-all-microservices"
Enter fullscreen mode Exit fullscreen mode

auth_service/auth.py

import jwt
import time
from django.conf import settings

JWT_ALGORITHM = "HS256"
JWT_EXP_SECONDS = 300

def issue_token(user_id: str) -> str:
    payload = {
        "sub": user_id,
        "iat": int(time.time()),
        "exp": int(time.time()) + JWT_EXP_SECONDS,
    }

    token = jwt.encode(
        payload,
        settings.DJANGO_SECRET_KEY,
        algorithm=JWT_ALGORITHM,
    )
    return token
Enter fullscreen mode Exit fullscreen mode

views.py

from django.http import JsonResponse
from .auth import issue_token

def login(request):
    user_id = "123"  # assume validated user
    token = issue_token(user_id)
    return JsonResponse({"access_token": token})
Enter fullscreen mode Exit fullscreen mode

This service only issues jwt tokens. When a user sends email and password to login the correct user.


Service 2, Business Service

This is a separate Django project, deployed independently.
Its database doesn't have users or auth related stuff.
It only knows how to read the content of auth service JWT token because of same django_secret_key.

settings.py

DJANGO_SECRET_KEY = "same-secret-between-all-microservices"
Enter fullscreen mode Exit fullscreen mode

Custom JWT Middleware

middleware.py

import jwt
from django.conf import settings
from django.http import JsonResponse

class JWTAuthMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        auth_header = request.headers.get("Authorization")

        if not auth_header or not auth_header.startswith("Bearer "):
            return JsonResponse({"detail": "Unauthorized"}, status=401)

        token = auth_header.split(" ")[1]

        try:
            payload = jwt.decode(
                token,
                settings.DJANGO_SECRET_KEY,
                algorithms=["HS256"],
            )
        except jwt.ExpiredSignatureError:
            return JsonResponse({"detail": "Token expired"}, status=401)
        except jwt.InvalidTokenError:
            return JsonResponse({"detail": "Invalid token"}, status=401)

        request.user_id = payload["sub"]
        return self.get_response(request)
Enter fullscreen mode Exit fullscreen mode

Register Middleware

settings.py

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "your_app.middleware.JWTAuthMiddleware",
]
Enter fullscreen mode Exit fullscreen mode

Protected View in Service 2

from django.http import JsonResponse

def dashboard(request):
    return JsonResponse({
        "message": "Access granted",
        "user_id": request.user_id,
    })
Enter fullscreen mode Exit fullscreen mode

How the services talk

  1. Client calls Auth Service → receives JWT
  2. Client calls Business Service with Authorization: Bearer <token>
  3. Business Service validates token locally
  4. No shared DB, no shared sessions, no Django apps crossing boundaries

That separation is what makes it a microservice. Separate apps, separate databases, linked together based on good old keys.


The important clarification

  • Multiple Django apps in one project is not microservices
  • One Django project per service is the minimum bar
  • Auth must be explicit and stateless
  • Token validation happens in every service

That is the real line between a Django app and a Django microservice.

Top comments (0)