diff --git a/_authors/raghum99.md b/_authors/raghum99.md new file mode 100644 index 0000000..efd4aef --- /dev/null +++ b/_authors/raghum99.md @@ -0,0 +1,7 @@ +--- +username: raghum99 +name: Raghu M +logo: https://avatars.githubusercontent.com/u/102198853?v=4 +web: https://softpro9.com/full-stack-developer-course-bangalore +description: "I run technical training at SoftPro9 in Bangalore." +--- diff --git a/_posts/community/2026-09-21-python-django-vs-node-express-backend-comparison.md b/_posts/community/2026-09-21-python-django-vs-node-express-backend-comparison.md new file mode 100644 index 0000000..e886aa9 --- /dev/null +++ b/_posts/community/2026-09-21-python-django-vs-node-express-backend-comparison.md @@ -0,0 +1,166 @@ +--- +layout: post +title: "Python/Django vs. Node/Express: Choosing a Backend as a Beginner" +date: 2026-09-21 +authors: + - raghum99 +categories: [ community, python, backend ] +description: "A practical, honest comparison of Django and Node/Express for beginners choosing their first backend, with a real side-by-side CRUD API in both." +excerpt: "Most beginners don't choose a backend based on the language - they choose based on which tutorial they found first. Here's what you're actually trading off once you're past Hello World, with the same small API built both ways." +featured: false +--- + +Most beginners don't choose a backend based on the language - they choose based on which tutorial they found first. That's a fine way to start, but it's worth understanding what you're actually trading off once you're past "Hello World." This post builds the same small API - a list of tasks, with create/read/update/delete - in Django and in Node/Express, then compares what's actually different. + +## The setup + +**Django** ships with an ORM, a migrations system, an admin panel, and authentication in the base install. For a JSON API specifically, the ecosystem's standard tool is [Django REST Framework](https://www.django-rest-framework.org/) (DRF) - it isn't part of core Django, but it's the de facto default for building APIs with it, and it's what this comparison uses. + +**Node/Express** is a minimal HTTP layer by design - routing and middleware, nothing else assumed. There's no bundled ORM, admin panel, or auth system; you pick each piece yourself. This example pairs it with [Mongoose](https://mongoosejs.com/) for MongoDB, since that's the common beginner path (and matches the "M" in MERN), but Express works just as well in front of PostgreSQL via an ORM like Prisma or Sequelize. + +## The same API, both ways + +Both versions expose: list tasks, create a task, get one task, update one, delete one. + +### Django REST Framework + +```python +# models.py +from django.db import models + +class Task(models.Model): + title = models.CharField(max_length=200) + done = models.BooleanField(default=False) + + +# serializers.py +from rest_framework import serializers +from .models import Task + +class TaskSerializer(serializers.ModelSerializer): + class Meta: + model = Task + fields = ["id", "title", "done"] + + +# views.py +from rest_framework import viewsets +from .models import Task +from .serializers import TaskSerializer + +class TaskViewSet(viewsets.ModelViewSet): + queryset = Task.objects.all() + serializer_class = TaskSerializer + + +# urls.py +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import TaskViewSet + +router = DefaultRouter() +router.register("tasks", TaskViewSet) + +urlpatterns = [ + path("", include(router.urls)), +] +``` + +That's the whole thing. `ModelViewSet` + `DefaultRouter` gives you list, create, retrieve, update, and delete on `/tasks/` and `/tasks//` automatically - no route is written by hand. Run `python manage.py makemigrations && python manage.py migrate` and the `Task` table exists; the admin panel (`/admin/`) gets a working CRUD UI for it for free if you register the model there too. + +### Node/Express + Mongoose + +```javascript +// models/Task.js +import mongoose from "mongoose"; + +const taskSchema = new mongoose.Schema({ + title: { type: String, required: true }, + done: { type: Boolean, default: false }, +}); + +export default mongoose.model("Task", taskSchema); + + +// routes/tasks.js +import express from "express"; +import Task from "../models/Task.js"; + +const router = express.Router(); + +router.get("/tasks", async (req, res) => { + const tasks = await Task.find(); + res.json(tasks); +}); + +router.post("/tasks", async (req, res) => { + const task = await Task.create(req.body); + res.status(201).json(task); +}); + +router.get("/tasks/:id", async (req, res) => { + const task = await Task.findById(req.params.id); + if (!task) return res.status(404).json({ error: "Not found" }); + res.json(task); +}); + +router.put("/tasks/:id", async (req, res) => { + const task = await Task.findByIdAndUpdate(req.params.id, req.body, { new: true }); + res.json(task); +}); + +router.delete("/tasks/:id", async (req, res) => { + await Task.findByIdAndDelete(req.params.id); + res.status(204).end(); +}); + +export default router; + + +// app.js +import express from "express"; +import mongoose from "mongoose"; +import taskRoutes from "./routes/tasks.js"; + +await mongoose.connect(process.env.MONGO_URI); + +const app = express(); +app.use(express.json()); +app.use("/", taskRoutes); + +app.listen(3000); +``` + +Every route here is explicit - you can see exactly what happens on each request, in order, with nothing generated behind the scenes. That's five handwritten route handlers versus zero in the Django version. + +## What that difference actually means + +| | Django REST Framework | Node/Express + Mongoose | +|---|---|---| +| Lines to a working CRUD API | ~20 (no routes written by hand) | ~40 (every route explicit) | +| Routing | Generated by the router from the viewset | Written by hand, one handler per route | +| Admin UI | Included, free, for any registered model | Not included - build your own or skip it | +| Auth | Built into Django; DRF adds token/session auth on top | Pick a library (Passport, custom JWT, etc.) and wire it in | +| Schema/validation | Model fields + serializer validation | Mongoose schema validation, or a separate library (Zod, Joi) | +| Language across the stack | Python backend, JS frontend - a context switch | Same language (JS/TS) frontend and backend | +| What you're trading | Less code to write, but more "magic" to learn to read | More code to write, but every line is visible | + +The DRF version isn't shorter because Django is "better" - it's shorter because a `ModelViewSet` is doing route generation, serialization, and validation for you based on conventions. That's a genuine time-saver once you know the conventions, and a genuine source of confusion before you do, because the routing logic isn't sitting in front of you the way `router.get("/tasks", ...)` is. + +## When Django is the better fit + +- You're already comfortable in Python and don't want to context-switch languages for the backend. +- You want an admin UI without building one - useful for internal tools, content-heavy apps, or anything where non-developers need to edit data. +- The app needs a lot of "standard" CRUD surface area quickly, and you're fine leaning on DRF's conventions instead of writing every route. +- You value having auth, migrations, and an ORM already integrated and tested together, rather than assembled from separate packages. + +## When Node/Express is the better fit + +- Your frontend is already React, Vue, or similar, and you'd rather write one language end-to-end than switch between JS and Python on the same feature. +- You want to see and control every request handler explicitly, without a framework generating routes on your behalf - useful while you're still learning what a route actually does. +- The API is small, custom, or doesn't map cleanly onto CRUD-on-a-model - Express's lack of opinions is an advantage once your endpoints stop looking like "list/create/update/delete on a table." +- You want to choose your own ORM/validation/auth stack piece by piece rather than take a framework's defaults. + +## The honest trade-off + +Django's structure is a strength until it isn't - you'll move fast on anything that fits the `ModelViewSet` shape, and fight the framework a little on anything that doesn't. Express's minimalism means you assemble more yourself up front, which is either flexibility or busywork depending on the day and the deadline. Neither is a wrong first choice; the better question is usually "which language do I already know, and what's my frontend built in" rather than "which framework is objectively better."