← Back to list

Node.js 101 (Express) แบบฉบับสาย Django REST เข้าใจได้อย่างรวดเร็ว

เริ่มต้นแบบนี้ดีกว่าคับ ลองดูตารางเปรียบเทียบโลก 2 ใบนี้ดู จะเข้าใจ environment ประมาณนึงล่ะ

Grassroot Engineer · 2026-06-06 17:06 · 0 claps · 6.1 min read paywalled
#nodejs
Open on Medium ↗
Wiki topics: 🌐 · Web Development 📰 · Journalism & News

Node.js 101 (Express) แบบฉบับสาย Django REST เข้าใจได้อย่างรวดเร็ว

เริ่มต้นแบบนี้ดีกว่าคับ ลองดูตารางเปรียบเทียบโลก 2 ใบนี้ดู จะเข้าใจ environment ประมาณนึงล่ะ

Django เป็น “batteries included” (มากับทุกอย่างในกล่องเดียว) แต่ฝั่ง Node คุณ “ประกอบเอง” จากชิ้นส่วนเล็กๆ มากกว่า ยกเว้นถ้าเราเลือก NestJS ซึ่งจะให้ความรู้สึกใกล้เคียง DRF มากที่สุด

+----------------------------+--------------------------------------+----------------------------------------------+
| Django / Python            | Node.js                              | หมายเหตุ                                      |
+----------------------------+--------------------------------------+----------------------------------------------+
| Python                     | JavaScript / TypeScript              | ภาษา                                         |
| CPython                    | Node.js / V8                         | ตัวรันโค้ด (Runtime)                            |
| pip                        | npm / pnpm / yarn                    | ตัวจัดการ Package                              |
| requirements.txt           | package.json                         | รายการ Dependency                            |
| virtualenv / venv          | node_modules/                        | Node เก็บ package ในโปรเจกต์เสมอ ไม่ต้อง activate|         |
| pyenv                      | nvm / fnm                            | จัดการเวอร์ชัน Runtime                          |
| Django                     | Express / Fastify / NestJS           | Framework                                    |
| Django REST Framework      | NestJS (ใกล้สุด)                       | REST API Framework                           |
| manage.py runserver        | node --watch index.js                | Dev Server                                   |
| urls.py                    | router / app.get(...)                | กำหนด Route                                  |
| views.py                   | Route Handlers / Controllers         | Logic รับ Request                             |
| models.py + Django ORM     | Prisma / Drizzle / TypeORM           | ORM                                          |
| serializers.py             | Zod / class-validator                | Validation / Serialization                   |
| middleware                 | middleware                           | แนวคิดเหมือนกัน                                 |
| settings.py                | .env + Config Files                  | Configuration                                |
| makemigrations + migrate   | prisma migrate dev                   | Database Migration                           |
| gunicorn / uwsgi           | pm2 / cluster / process manager      | Production Process Management                |
+----------------------------+--------------------------------------+----------------------------------------------+

1. Node.js คืออะไร และมาจากไหน

ก่อนอื่นต้องแยกให้ออกก่อน เพราะคนชอบสับสนกัน:

  • JavaScript คือ “ภาษา” (เหมือน Python)
  • Node.js คือ “runtime” ที่เอาไว้รันภาษา JavaScript นอก browser (เหมือน CPython ที่เอาไว้รัน Python) Node.js ไม่ใช่ภาษา และ ไม่ใช่ framework มันคือโปรแกรมที่เอา V8 (เครื่องยนต์ JavaScript ตัวเดียวกับที่อยู่ใน Google Chrome) มาทำให้รันบนเครื่อง server ได้

เสน่ห์ใหญ่ที่สุดของมันคือ: ทีมหนึ่งสามารถใช้ภาษาเดียว (JavaScript) ได้ทั้ง frontend และ backend

2. ทำไม Node ถึงเร็ว (และจุดอ่อนอยู่ตรงไหน)

นี่คือหัวใจที่ต้องเข้าใจ เพราะมัน “คิดคนละแบบ” กับ original Django

โมเดลของ Django แบบดั้งเดิม (sync / WSGI)

ใน Django ปกติ(รันบน gunicorn หรือ uwsgi) แต่ละ request จะถูกจับคู่กับ worker หนึ่งตัว (process หรือ thread) เมื่อ view ของคุณไปเรียก database หรือยิง HTTP ออกไปข้างนอก worker ตัวนั้นจะ หยุดรอ (block) อยู่เฉยๆ จนกว่างานนั้นจะเสร็จ ระหว่างนั้นมันทำอะไรอย่างอื่นไม่ได้เลย

ถ้าอยากรับ request พร้อมกันได้มากขึ้น คุณก็เพิ่มจำนวน worker (เช่น gunicorn -w 8) ซึ่งแต่ละตัวก็กินแรมเพิ่มขึ้นเรื่อยๆ คิดว่า Django dev รู้ดีแหละ

โมเดลของ Node (async / event loop)

Node ใช้ thread หลักเพียงเส้นเดียว รันสิ่งที่เรียกว่า event loop เมื่อโค้ดวิ่งไปเจองาน I/O (query database, เรียก API, อ่านไฟล์) Node จะ ไม่หยุดรอ แต่จะลงทะเบียน callback ไว้แล้ววิ่งไปทำงานอื่นต่อทันที พองาน I/O นั้นเสร็จค่อยกลับมาทำ callback ต่อ

ผลคือ thread เส้นเดียวสามารถดูแล connection ได้เป็นพันๆ พร้อมกัน ตราบใดที่งานส่วนใหญ่เป็นการ “รอ” (ซึ่งงาน web API ส่วนใหญ่ก็เป็นแบบนั้น) จริงๆมันคือ concept เดียวกับ FastAPI เลยนะ (ฉะนั้น node เร็วกว่ามากนะ ในแง่ของ concurrency เพราะเหมาะกับงาน I/O-bound อยู่แล้ว)

ลองนึกภาพร้านอาหารดู

Django แบบ sync คือพนักงานเสิร์ฟ 1 คนต่อ 1 โต๊ะ
พนักงานต้องยืนเฝ้าโต๊ะตั้งแต่รับออเดอร์ รอครัวทำ จนเสิร์ฟเสร็จ
ถึงจะไปรับโต๊ะใหม่ได้ ถ้าจะรับลูกค้าเพิ่มก็ต้องจ้างพนักงานเพิ่ม (คือเพิ่ม worker)

Node คือพนักงานคนเดียวรับออเดอร์จากทุกโต๊ะรัวๆ พอส่งออเดอร์ให้ครัว (คือ I/O)
ก็เดินไปรับโต๊ะถัดไปทันที ไม่ยืนรอ พอครัวทำเสร็จค่อยกลับมาเสิร์ฟ 
พนักงานคนเดียวจึงดูแลได้หลายสิบโต๊ะพร้อมกัน

แต่จุดอ่อนอยู่ตรงนี้: ถ้าพนักงาน (thread เส้นเดียว) ต้องลงไปหั่นผักเองนาน 5 นาที ทุกโต๊ะในร้านจะหยุดรอหมด เพราะมีพนักงานคนเดียว

แปลเป็นเทคนิคคือ ถ้าคุณเอางาน **CPU-bound หนักๆ** (เช่น ประมวลผลรูปภาพขนาดใหญ่, เข้ารหัสหนักๆ, loop คำนวณมหาศาล) มาวางบน event loop ทั้งระบบจะค้างรอจนกว่างานนั้นจะเสร็จ

ทางแก้คือแยกงานหนักออกไปทำที่ worker threads หรือแยกเป็น service ต่างหาก แต่หลักการให้จำง่ายๆ คือ **Node เกิดมาเพื่องาน I/O-heavy ไม่ใช่ CPU-heavy**

3. เกริ่นเยอะแล้ว มาเล่น NodeJS ของจริงกันเลยดีกว่า

  • ติดตั้ง node, nvm, npm ให้เรียบร้อยนะ ตรวจสอบได้ด้วย
node --version   # ควรขึ้น v24.x.x
npm --version    # npm ติดมากับ Node อยู่แล้ว
  • Create project
mkdir my-api && cd my-api
npm init -y       # สร้างไฟล์ package.json ให้อัตโนมัติ
  • ปรับ package.json ให้ modern ขึ้นจ้า
▪️ "type": "module" ทำให้เราเขียน import / export ได้
  (สไตล์สมัยใหม่ คล้าย import ของ Python) แทนการเขียน require() แบบเก่า

▪️ node --watch คือ auto-reload เวลาแก้โค้ด (สมัยก่อนต้องลง package ชื่อ nodemon
  เดี๋ยวนี้ Node มีให้ในตัวแล้ว) เทียบได้กับ runserver ที่ reload ให้อัตโนมัติ

▪️ --env-file=.env ทำให้ Node อ่านไฟล์ .env ได้ในตัว (เทียบ settings.py ที่อ่านค่าจาก environment)
- scripts พวกนี้เรียกใช้ด้วย npm run dev และ npm start

  • ติดตั้ง Express
npm install express

หลังรันคำสั่งนี้คุณจะเห็นโฟลเดอร์ node_modules/ กับไฟล์ package-lock.json โผล่ขึ้นมา

จุดที่ต่างจาก Python คือ node_modules/ จะอยู่ในโฟลเดอร์โปรเจกต์เสมอ (เป็น local โดยธรรมชาติ) คุณไม่ต้อง activate อะไรเหมือน virtualenv และไม่ต้องห่วงว่ามันจะไปปนกับโปรเจกต์อื่น ส่วน package-lock.json ทำหน้าที่ล็อกเวอร์ชันเป๊ะๆ เหมือน pip freeze หรือ lock file

node_modules + package-lock.json โผล่มาล่ะจ้า

node_modules + package-lock.json โผล่มาล่ะจ้า

  • สร้างไฟล์ src/index.js + .env แล้วรันด้วย npm run dev ดูนะ
// src/index.js
import express from 'express';

const app = express();

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Node.js na ja!' });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

npm run dev

npm run dev

สังเกตว่าใน Express ทุกอย่างเชื่อมโยงกันในที่เดียว: app.get('/', handler) คือการรวม urls.py (เส้นทาง /) กับ views.py (ฟังก์ชันที่ทำงาน) เข้าด้วยกัน

ตัวแปร req กับ res คือพระเอกของฝั่ง Express:

  • req (request) เก็บข้อมูลที่ client ส่งมา เทียบกับ request ใน Django
  • res (response) คือตัวที่เราใช้ส่งข้อมูลกลับ เทียบกับการ return Response(...) ใน DRF แต่ใน Express เรา "เรียก method" บน res แทนการ return (เช่น res.json(...), res.status(404))

4. มาสร้าง REST API แบบ CRUD เปรียบเทียบกันดูนะ ระหว่าง Django REST Framework กับ Node.js (Express)

  • สร้างไฟล์ src/books.routes.js (ตอนนี้เก็บข้อมูลในตัวแปรไปก่อน เดี๋ยวค่อยต่อ database):
// src/books.routes.js
import { Router } from 'express';
import { randomUUID } from 'node:crypto';

const router = Router();
let books = []; // เก็บใน memory ไปก่อนเพื่อความง่าย

// GET /books  (list)
router.get('/', (req, res) => {
  res.json(books);
});

// GET /books/:id  (retrieve)
router.get('/:id', (req, res) => {
  const book = books.find((b) => b.id === req.params.id);
  if (!book) {
    return res.status(404).json({ error: 'Book not found' });
  }
  res.json(book);
});

// POST /books  (create)
router.post('/', (req, res) => {
  const book = { id: randomUUID(), ...req.body };
  books.push(book);
  res.status(201).json(book);
});

// PUT /books/:id  (update)
router.put('/:id', (req, res) => {
  const index = books.findIndex((b) => b.id === req.params.id);
  if (index === -1) {
    return res.status(404).json({ error: 'Book not found' });
  }
  books[index] = { ...books[index], ...req.body };
  res.json(books[index]);
});

// DELETE /books/:id
router.delete('/:id', (req, res) => {
  books = books.filter((b) => b.id !== req.params.id);
  res.status(204).end();
});

export default router;

แล้วเอามาต่อใน src/index.js:

// src/index.js
import express from 'express';
import booksRouter from './books.routes.js';

const app = express();

app.use(express.json());        // อ่าน JSON body (มีมาในตัว Express 5)
app.use('/books', booksRouter); // mount router ทั้งหมดไว้ใต้ /books

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

ทดสอบได้เลย:

# สร้างหนังสือใหม่
curl -X POST http://localhost:3000/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Clean Code","author":"Robert Martin","year":2008}'

# ดูทั้งหมด
curl http://localhost:3000/books

DRF ด้านล่างเขียนน้อยนะแต่ต้องเข้าใจ convention (แบบแผน) ส่วน Express ให้คุณเขียนเยอะแต่ควบคุมได้ทุกอย่าง (ถ้าอยากได้ความ “เขียนน้อย” แบบ DRF ในโลก Node ให้ข้ามไปดู NestJS จ้า

รูปด้านล่างเปรียบเทียบระหว่าง Django vs Express แบบเข้าใจง่ายๆนะ (ทำความเข้าใจได้เยย)

+--------------------+-----------------------------------+-----------------------------+
|                    | Django REST Framework             | Express                     |
+--------------------+-----------------------------------+-----------------------------+
| จำนวนโค้ดสำหรับ CRUD | สั้นมาก (ModelViewSet)              | ยาวกว่า เขียนทีละ endpoint     |
+--------------------+-----------------------------------+-----------------------------+
| ความ "เห็นชัด"       | มีเวทมนตร์ซ่อน ต้องรู้ convention       | เห็นทุกอย่างที่เกิดขึ้น             |
+--------------------+-----------------------------------+-----------------------------+
| ความยืดหยุ่น          | ต้อง override เมื่อต้องการแก้ behavior | ปรับอะไรก็ได้ตั้งแต่แรก           |
+--------------------+-----------------------------------+-----------------------------+
| เหมาะกับ            | งานมาตรฐาน CRUD ที่อยากเร็ว          | งานที่ต้องคุม flow เอง          |
+--------------------+-----------------------------------+-----------------------------+

Django vs Express

Django vs Express

End !!!

More things

  • ใน Django แบบ sync เวลาคุณ query database คุณเขียนตรงๆ ได้เลย แล้วโค้ดจะหยุดรอจนได้ผลลัพธ์:
def get(self, request):
    books = Book.objects.all()   # โค้ดหยุดรอตรงนี้จนกว่าจะได้ข้อมูล
    serializer = BookSerializer(books, many=True)
    return Response(serializer.data)
  • ใน Node งาน I/O แทบทุกอย่างเป็น async คุณต้องใส่คำว่า await หน้ามัน แต่ระหว่างที่ await thread หลักจะไม่ได้หยุดรอเฉยๆ มันจะไปรับงานอื่นต่อ (นี่คือเหตุผลที่ Node รับ request พร้อมกันได้เยอะ):
router.get('/', async (req, res) => {
  const books = await db.book.findMany(); // ไม่ block thread ระหว่างรอ
  res.json(books);
});

NodeJS จะใช้ Prisma แทน ORM ใน Django นะ

  • แต่ต้องติดตั้งแบบนี้
npm install prisma @prisma/client
npx prisma init
  • Prisma ใช้ไฟล์ schema ของตัวเองชื่อ schema.prisma:
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Book {
  id     String @id @default(uuid())
  title  String
  author String
  year   Int
}

ซึ่งเทียบกับ models.py ของ Django จะเป็นแบบนี้จ้า

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    year = models.IntegerField()

ส่วน Migration (เทียบ makemigrations + migrate) โดย คำสั่งเดียวนี้เทียบได้กับ python manage.py makemigrations ตามด้วย migrate คือสร้างไฟล์ migration และ apply ลง database ให้เลย

npx prisma migrate dev --name init

Validation / Serialization ด้วย Zod (เทียบ DRF Serializer)

ใน DRF serializer ทำสองงานพร้อมกัน: ตรวจสอบข้อมูล (validation) และแปลงข้อมูล (serialization) เมื่อเรียก serializer.is_valid()

ฝั่ง Node ตัวที่นิยมที่สุดสำหรับ validation คือ Zod ซึ่งทำให้คุณกำหนด “รูปร่างที่ถูกต้อง” ของข้อมูลได้อย่างชัดเจน

npm install zod
// ใน Express

import { z } from 'zod';
import { randomUUID } from 'node:crypto';

// นิยาม schema (เทียบ BookSerializer)
const BookSchema = z.object({
  title: z.string().min(1),
  author: z.string().min(1),
  year: z.number().int().gte(0),
});

router.post('/', (req, res) => {
  const result = BookSchema.safeParse(req.body);

  // เทียบ serializer.is_valid()
  if (!result.success) {
    return res.status(400).json({ errors: result.error.flatten() });
  }

  // result.data คือข้อมูลที่ผ่านการตรวจและแปลงแล้ว (เทียบ serializer.validated_data)
  const book = { id: randomUUID(), ...result.data };
  books.push(book);
  res.status(201).json(book);
});
# เทียบกับใน Django จ้า

serializer = BookSerializer(data=request.data)
if not serializer.is_valid():
    return Response(serializer.errors, status=400)
book = serializer.save()

แนวคิดตรงกันเป๊ะ: นิยาม schema ก่อน แล้วเอาข้อมูลที่เข้ามาไปเช็กกับ schema นั้น ถ้าไม่ผ่านก็ตอบ 400 พร้อม error

สรุป

ภาพรวมทั้งหมด:

  1. Node.js คือ runtime ที่เอา JavaScript ออกมารันบน server ได้ (ไม่ใช่ภาษา ไม่ใช่ framework)
  2. มันเร็วกับงาน I/O เพราะใช้ event loop แบบ non-blocking thread เดียวรับ connection ได้เป็นพัน แต่ ไม่เหมาะกับงาน CPU หนัก บน thread หลัก
  3. ทุกอย่างใน Django มีของเทียบในฝั่ง Node: pip คือ npm, ORM คือ Prisma, serializer คือ Zod, middleware ก็คือ middleware
  4. Express ให้คุณเขียนเองทุกอย่าง (เห็นชัด ยืดหยุ่น) ส่วน NestJS ใกล้ DRF ที่สุด (มาพร้อมโครง)
  5. async / await คือ mindset ที่ต้องเปลี่ยน: งาน I/O แทบทุกอย่างต้อง await

ถ้าอยากได้ convention ที่ชัดเจนแบบ Django (มีโครงให้เป๊ะๆ ไม่ต้องคิดเอง) คำตอบคือ NestJS ในหัวข้อถัดไป

References:

If you think it’s useful for you, just clap your hands 👏 to be encouraged me.

GRASSROOT ENGINEER 😘


메타데이터
post_id
8dcd5890cc4f
slug
node-js-101-express-แบบฉบับสาย-django-rest-เข้าใจได้อย่างรวดเร็ว-8dcd5890cc4f
url
https://medium.com/@grassrootengineer/node-js-101-express-%E0%B9%81%E0%B8%9A%E0%B8%9A%E0%B8%89%E0%B8%9A%E0%B8%B1%E0%B8%9A%E0%B8%AA%E0%B8%B2%E0%B8%A2-django-rest-%E0%B9%80%E0%B8%82%E0%B9%89%E0%B8%B2%E0%B9%83%E0%B8%88%E0%B9%84%E0%B8%94%E0%B9%89%E0%B8%AD%E0%B8%A2%E0%B9%88%E0%B8%B2%E0%B8%87%E0%B8%A3%E0%B8%A7%E0%B8%94%E0%B9%80%E0%B8%A3%E0%B9%87%E0%B8%A7-8dcd5890cc4f
canonical_url
https://medium.com/@grassrootengineer/node-js-101-express-%E0%B9%81%E0%B8%9A%E0%B8%9A%E0%B8%89%E0%B8%9A%E0%B8%B1%E0%B8%9A%E0%B8%AA%E0%B8%B2%E0%B8%A2-django-rest-%E0%B9%80%E0%B8%82%E0%B9%89%E0%B8%B2%E0%B9%83%E0%B8%88%E0%B9%84%E0%B8%94%E0%B9%89%E0%B8%AD%E0%B8%A2%E0%B9%88%E0%B8%B2%E0%B8%87%E0%B8%A3%E0%B8%A7%E0%B8%94%E0%B9%80%E0%B8%A3%E0%B9%87%E0%B8%A7-8dcd5890cc4f
author_url
https://medium.com/@grassrootengineer
status
ok
fetched_at
2026-06-09 15:37:30