Back to Projects

Blog System Design Sandbox

ReactDjango REST FrameworkDockerGitHub ActionsAWS
Blog System Design Sandbox

Project Overview

Built a full-stack blogging system to experiment with production backend patterns including JWT access/refresh tokens, request validation, structured logging, health checks, and Dockerized deployment.

Fullstack Blog: System Design Sandbox

  1. Problem Statement

Most tutorial projects demonstrate features but ignore the operational realities that make production systems difficult:

Authentication lifecycle management

Failure handling and observability

Infrastructure consistency

Deployment reproducibility

API evolution without breaking clients

Multi-device session control

As a result, many portfolio projects appear feature-complete but lack engineering maturity.

The goal of this project was to build a minimal domain (blog) but stress-test the infrastructure, reliability, and system design aspects of a real production system.

The blog domain exists purely as a stable surface area to exercise concerns such as authentication, API behavior, deployment pipelines, and state management.

  1. System Goals

The system was designed around five engineering goals.

2.1 Authentication Lifecycle Control

JWT systems often fail in real deployments because tokens cannot easily be revoked or invalidated.

This system implements:

Access / Refresh token separation

Token versioning

Forced invalidation on password change

Blacklist tracking via Redis

This prevents long-lived compromised tokens from remaining valid.

2.2 Multi-Device Session Awareness

Typical JWT implementations treat authentication as stateless, which prevents device-level session control.

This project introduces a session tracking layer allowing users to:

View active sessions

Logout from specific devices

Logout from all devices

Revoke refresh tokens safely

Each login creates a UserSession record which maps refresh tokens to devices.

2.3 Operational Traceability

In production debugging, the most expensive failure is one that cannot be traced.

The system introduces request-scoped tracing:

Every incoming request is assigned a request ID that flows through:

client → nginx → django middleware → logs

Structured logs attach:

request_id user_id endpoint status_code latency

This allows failed requests to be traced across services.

2.4 Deployment Reproducibility

A frequent source of production incidents is environment drift.

The project ensures environment parity through containerization.

Local Dev Docker Containers Production Infrastructure

The same Docker configuration runs in both environments.

This guarantees that:

“Works on my machine” means works in production.

2.5 Safe API Evolution

Production APIs cannot change destructively.

This system enforces additive API design:

Versioned endpoints

Backward compatible schema changes

Standard response envelopes

Example API response structure:

{ "data": {...}, "meta": {...}, "error": null }

Clients remain stable even when backend fields evolve.

  1. Architecture Overview

The application follows a decoupled full-stack architecture.

Client (React + Vite) │ ▼ Nginx Reverse Proxy │ ▼ Django REST API │ ├── PostgreSQL (persistent data) └── Redis (token blacklist + caching) Key Architectural Decisions

Frontend / Backend Separation

Benefits:

Independent deployment

API-first design

Better scaling options

Easier mobile integration

Reverse Proxy Layer (Nginx)

Responsibilities:

TLS termination

Static asset delivery

Routing requests to backend

Request buffering and protection

Redis Integration

Used for:

JWT blacklist

Future caching strategies

Rate-limiting storage

Redis was selected due to its low latency and atomic operations.

  1. Technology Stack Backend

Framework: Django 5 + Django REST Framework

Authentication: SimpleJWT

Database: PostgreSQL

Caching: Redis

API Documentation: drf-spectacular

Frontend

Framework: React 19

Build Tool: Vite

Styling: Tailwind CSS

Routing: React Router v7

HTTP Client: Axios

Infrastructure

Docker

Docker Compose

AWS EC2

AWS RDS

GitHub Actions CI

  1. Authentication System Design

JWT authentication is implemented with additional security layers.

Token Structure

Two tokens are issued during authentication.

Access Token Short lifespan Used for API requests

Refresh Token Longer lifespan Used to obtain new access tokens Token Versioning Strategy

Each user has a token_version field.

When a sensitive event occurs (e.g., password change):

token_version += 1

All previously issued tokens become invalid because their version no longer matches the user record.

This prevents token reuse after credential compromise.

Token Blacklisting

Refresh tokens are stored in Redis when revoked.

Flow:

Logout → token added to Redis blacklist Future refresh attempt → token rejected

This ensures refresh tokens cannot be reused.

  1. Session Management Model

Unlike traditional JWT systems, sessions are persisted.

Each login creates:

UserSession

user_id device ip_address created_at refresh_token is_active

This allows:

Device-specific logout

Session auditing

Security visibility

  1. Resilience & Failure Handling

Production systems must assume failure.

Several defensive mechanisms were implemented.

Soft Deletes

Instead of removing records:

deleted_at timestamp

This prevents accidental data loss and allows recovery.

API Timeouts

Axios interceptors enforce request timeouts to prevent UI lockups.

Centralized Error Handling

React error boundaries catch runtime UI failures.

ErrorBoundary

Prevents entire application crashes.

  1. CI/CD Pipeline

GitHub Actions enforce automated checks before deployment.

Pipeline stages:

Lint Tests Build Docker Image Deployment

This ensures broken code cannot be merged into the main branch.

  1. Observability Strategy

Observability was designed around three pillars.

Logging

Structured logs capture:

timestamp request_id endpoint user status latency Health Endpoints

Infrastructure monitoring uses:

/health/ /health/db/ /health/cache/

These endpoints allow load balancers and monitoring tools to verify system health.

Failure Visibility

Centralized error mapping ensures the frontend receives meaningful error states instead of silent failures.

  1. Security Considerations

Several security protections were implemented.

Rate Limiting

Authentication endpoints enforce request throttling.

POST /auth/token/

This reduces brute-force login attempts.

Environment Validation

Startup checks verify required environment variables.

If critical variables are missing:

Application refuses to start

Fail-fast prevents silent configuration bugs.

Secrets Management

Sensitive values such as:

SECRET_KEY DATABASE_URL JWT_SECRET

are stored in environment variables rather than source code.

  1. Project Structure

The repository is organized to enforce separation of concerns.

backend/ accounts/ blog/ config/

frontend/ components/ pages/ context/

infrastructure/ docker-compose.yml

This structure allows the system to evolve without tightly coupling frontend and backend logic.

  1. Performance Considerations

Several performance improvements were implemented.

Lazy Loading

React routes are dynamically loaded.

React.lazy()

This reduces initial bundle size.

Axios Interceptors

Token attachment and refresh logic are centralized to avoid duplicated network logic across components.

Docker Layer Caching

Build layers were optimized to reduce CI build times.

  1. Key Engineering Lessons

Building the system revealed several practical lessons.

  1. Authentication is Hard

Token invalidation and multi-device logout introduce complexity rarely covered in tutorials.

  1. Observability is Critical

Without request tracing, debugging distributed failures becomes extremely difficult.

  1. Environment Consistency Matters

Containerization removed entire classes of deployment bugs.

  1. API Stability Must Be Planned Early

Versioning and response envelopes prevent frontend breakage during backend evolution.

  1. Future Engineering Work

Future work will focus on deeper infrastructure complexity.

Planned Enhancements

Advanced Caching

Redis read-through caching Cache invalidation strategies

Background Jobs

Introduce Celery workers for:

email sending analytics aggregation image processing

Observability Stack

Prometheus metrics Grafana dashboards Distributed tracing 15. Conclusion

This project intentionally avoids feature complexity and instead focuses on system behavior under real operational constraints.

The blog domain acts as a stable testing ground to explore engineering problems such as:

authentication lifecycle

infrastructure reliability

API evolution

deployment reproducibility

system observability

The result is a full-stack system designed not as a tutorial project, but as a system design sandbox for production-grade engineering practices.

Next-Gen Engineering

Ready to Build Your Scalable MVP?

Partner with Ashif E.K to transform your vision into a high-performance React & Django application. Let's engineer your success.

Schedule a Strategy Call

Limited availability for Q2 2026.

Syncing...