At KEHEM IT, our core engineering philosophy is simple: Craft software that outlasts trends and outperforms expectations.
We avoid chasing every fleeting JavaScript framework of the month. Instead, we invest in mature, production-hardened technologies that give our clients an unfair competitive advantage.
Our stack of choice for high-performance web applications is Django REST Framework (DRF) on the backend paired with Nuxt 3 (Vue 3) on the frontend.
Here is why this decoupled architecture wins.
A note on how to read this: the aim is not to argue that other stacks are bad. It is to explain the trade-offs we knowingly accepted, so that a technical buyer — or an in-house engineer inheriting one of our codebases — understands exactly what they are getting and why.
The Synergy of Python & Modern Vue
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ NUXT 3 (Vue 3) │ │ DJANGO REST FRAMEWORK │
│ • Universal SSR & SSG │ JSON │ • Battle-Tested ORM │
│ • Lightning Frontend Routing │◄──────►│ • Native AI & Data Tooling │
│ • Tailwind Minimalist UI │ API │ • Built-In Admin & Security │
└──────────────────────────────┘ └──────────────────────────────┘
The arrow in the middle is where most of the engineering care goes. A clean contract between those two boxes is what allows the frontend and backend to evolve independently — and it is the part that separates a maintainable decoupled app from two half-applications held together by hope.
1. Backend: Python's Industrial Strength
Django provides a rock-solid foundation: * Zero Database Spaghetti: Clean relational models with Django ORM. * Instant Back-Office Admin: Manage data immediately without writing boilerplate dashboards. * Native AI Ecosystem: Seamless integration with OpenAI, Claude, LangChain, and Python data pipelines.
How We Structure a DRF Backend
The framework does not impose much structure, so we impose our own. Standard conventions across our projects:
- Versioned API from day one (
/api/v1/...) even when there is only one client. Versioning later, while a mobile app is in users' hands, is far more painful than adding it at the start. - Serializers as the validation boundary. Business rules live in service functions, not in views; views validate, authorise, delegate and serialise.
- Consistent pagination and filtering with
django-filterand a single cursor or page-number convention, so the frontend never writes a bespoke parser for a new endpoint. - One error contract. Every failure returns the same JSON shape — a machine-readable code, a human message, and field-level details — which lets the Nuxt layer handle errors generically.
- An OpenAPI schema generated from the code with
drf-spectacular, which we use to generate TypeScript types for the frontend. This one practice catches an entire category of "the API changed and the UI silently broke" bugs, and it costs almost nothing to maintain. - Background work in Celery, with the same Redis instance serving as broker. If an endpoint takes longer than roughly 300ms, the work belongs in a worker and the API returns a job reference.
2. Frontend: Nuxt 3's Speed & SEO Superpowers
Nuxt 3 takes Vue's reactivity and adds: * Hybrid Rendering: Pre-render marketing pages statically (SSG) for instant 100/100 Core Web Vitals, while rendering authenticated app dashboards as dynamic client-side apps (SPA). * TypeScript Support: End-to-end type safety across components and composables. * Auto-Imports & Clean DX: Minimal boilerplate lets our engineering team ship features 2x faster.
Authentication Across a Decoupled Stack
This is the most common source of bugs in a Django + Nuxt architecture, and it is worth being specific about our approach:
- Session cookies in
httpOnly,Secure,SameSite=Laxform rather than tokens inlocalStorage. Anything inlocalStorageis readable by any script that ever runs on the page. - The Nuxt server layer proxies authenticated requests to Django, so the browser never holds an API credential and CORS becomes a non-issue.
- SSR requests forward the session cookie, with server-side checks for authenticated routes so an unauthenticated visitor never briefly sees a dashboard shell.
- CSRF handled deliberately — Django's CSRF token is issued to the Nuxt server layer and passed through on state-changing requests.
Hybrid Rendering in Practice
Marketing pages, blog posts and documentation are statically generated and served from the CDN — typically a 40–80ms time to first byte, and no server cost per visit. Pricing and landing pages that vary by region use SSR. The authenticated application is a client-side app after an initial server-rendered shell, because its data is user-specific and caching it would be pointless and risky.
What We Trade Away, Honestly
Every architectural choice has a bill attached. Ours:
| Trade-off | Effect | How we manage it |
|---|---|---|
| Two languages (Python + TypeScript) | A solo generalist cannot do everything | Clear API contract; shared OpenAPI types |
| Two deployables | Slightly more CI/CD to write | Docker Compose on one host, or two containers on one platform |
| API versioning overhead | Discipline required from day one | /api/v1/ and a deprecation policy in writing |
| SSR needs a Node runtime | More server cost than pure static | Static generation for marketing; SSR only where needed |
| Not the "fashionable" stack | Harder to hire React specialists | Vue developers are plentiful and often very experienced |
On the last point: the React ecosystem is larger, but it also churns faster. Libraries that were standard three years ago are now legacy in React land, and every major version brings migration work. Vue's ecosystem is smaller and considerably more stable, which is a better fit for software expected to live for a decade.
Why Not Next.js, Rails or Laravel?
We get asked, so here is the short version:
- Next.js: Excellent framework with an enormous ecosystem. The trade-off is churn — App Router, Server Actions and RSC have moved fast enough that patterns from two years ago are now discouraged. Fine if you enjoy living near the frontier.
- Rails or Laravel: Both give superb developer velocity, and Laravel in particular has a huge hosting ecosystem. Neither has Python's data and AI tooling, which matters to a growing share of our clients.
- Node on the backend: The unified-language argument is genuinely good. We keep Node where it is strong — websockets and real-time features — rather than using it for everything.
Our Standardised Stack
Consistency is what allows a small studio to deliver like a larger one. Across projects we standardise on:
| Layer | Choice | Why |
|---|---|---|
| Backend | Django + DRF on Python 3.12 | Mature, secure defaults, huge talent pool |
| Database | PostgreSQL 16 | JSONB, full-text search, extensions |
| Cache & queue | Redis + Celery | Same infrastructure serves both |
| Frontend | Nuxt 3 + TypeScript + Tailwind | SSR/SSG flexibility, stable ecosystem |
| Storage | S3-compatible object storage | Portable between providers |
| Reverse proxy | Nginx (or Caddy for auto-TLS) | Predictable, well understood |
| Packaging | Docker + Compose | Reproducible across environments |
| Monitoring | Sentry + uptime checks + structured logs | Errors caught before customers report them |
The value of a standard stack is not aesthetic. It means any engineer joining a project already understands the shape of the system, the deployment is boring, and the people who maintain it in three years have a genuine chance of hiring for it.
When We Recommend Something Else
Consistency should not turn into dogma. We will recommend a different architecture when the problem calls for it:
- A pure marketing site does not need Django at all — Nuxt alone, statically generated, or even a simpler static site generator.
- A small internal tool is usually Django admin plus a few custom actions, not a full decoupled frontend.
- A real-time product — collaborative editing, live telemetry — gets a Node or Go service alongside Django, precisely where websocket concurrency justifies it.
- A data-heavy analytics product may want a separate columnar warehouse and Python notebooks, with Django as the application layer.
- An existing team with deep expertise in another stack should probably keep it. Team velocity beats architecture diagrams.
Testing and Quality Gates
A decoupled stack needs discipline about the contract between the two halves, and tests are where that discipline lives. Our standard setup:
| Layer | Tool | What it protects against |
|---|---|---|
| Model & service logic | pytest-django with factory_boy | Business-rule regressions |
| API behaviour | DRF test client against every endpoint | Permission and validation bugs |
| Schema contract | OpenAPI snapshot test in CI | Silent breaking API changes |
| Frontend components | Vitest + Vue Test Utils | UI logic and state bugs |
| Critical journeys | Playwright: signup, checkout, core workflow | Integration failures users actually hit |
| Performance | Lighthouse CI budget on key routes | Slow pages creeping in over time |
Two practices matter more than tool choice. Run the schema contract test in CI and fail the build when a field is removed or its type changes — the frontend will not warn you, it will simply break in production. And keep end-to-end tests to a small, high-value set covering the journeys that generate revenue; a hundred brittle browser tests that everyone re-runs until they pass is worse than five reliable ones.
Deployment: Shipping the Two Halves Together
Two deployables means a release must be ordered thoughtfully.
- Database migrations go first, and they must be backwards compatible. Add columns before you write to them, deploy code that tolerates both old and new shapes, then remove the old column in a later release. Never ship a migration and code that depends on it in the same instant.
- The API is the contract, so the frontend deploys second. New endpoints and fields can ship without any frontend change; removing or renaming requires a deprecation window.
- Blue/green or rolling deploys with health checks, so a bad release does not take the app down between container restarts.
- One source of truth for environment variables — the Nuxt server layer reads its own configuration, and secrets never reach the browser bundle. A leaked
NUXT_PUBLIC_*value is the most common mistake in this architecture.
Related reading: The stack comparison is where this choice sits against the alternatives; multi-tenant architecture shows the same stack under tenancy; and real-time WebSockets covers the piece we reach for Node to handle. Experience the power of custom Django and Nuxt 3 software crafted for your business. Talk to KEHEM IT.
Want a stack opinion for your project? Send us the requirements and we will recommend a stack, not a favourite.
Have a project in mind?
KEHEM designs and builds thoughtful websites, SaaS products, and business systems.