บทที่ 5 เน้นการออกแบบ REST API ส่วนบทนี้จะลงมือมองฝั่ง server ว่า API ทำงานอย่างไรตั้งแต่รับ request ตรวจสอบข้อมูล ประมวลผล business logic ติดต่อแหล่งข้อมูล และส่ง response กลับไปยัง client เช่น Angular app หรือ Postman
คำสำคัญของบทเรียน
Back-end ทำหน้าที่รับ request จาก client แล้วประมวลผลตามกฎของระบบ เช่น ตรวจสอบข้อมูลสินค้า ตรวจสิทธิ์ผู้ใช้ คำนวณยอดรวม ติดต่อฐานข้อมูล และส่ง response กลับไปในรูปแบบ JSON
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#83a598',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart LR
A[Client
Angular/Postman] --> B[HTTP Request
คำขอ]
B --> C[Route
เลือก endpoint]
C --> D[Middleware
ตรวจและเตรียมข้อมูล]
D --> E[Controller
จัดการคำสั่ง]
E --> F[Service
Business Logic]
F --> G[Data Source
Database/API/File]
G --> F
F --> H[HTTP Response
JSON/Status Code]
H --> A
| ส่วน | หน้าที่ |
|---|---|
| Route | รับ URL และ HTTP method |
| Middleware | ทำงานก่อนถึง controller เช่น logging หรือ validation |
| Controller | รับ request และกำหนด response |
| Service | เก็บ business logic |
| Data Access | ติดต่อ database หรือแหล่งข้อมูล |
| Error Handler | จัดรูปแบบ error response |
Node.js ทำให้ JavaScript ทำงานฝั่ง server ได้ ส่วน Express ช่วยสร้าง web server และ REST API ได้ง่ายขึ้น โดยมีโครงสร้างหลักคือ app, route, middleware และ handler function
# สร้างโปรเจกต์ backend
mkdir product-api
cd product-api
npm init -y
# ติดตั้ง Express
npm install express
# เปิด server
node server.js
// server.js
const express = require('express');
const app = express();
const port = 3000;
// middleware สำหรับอ่าน JSON request body
app.use(express.json());
app.get('/api/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(port, () => {
console.log(`API server running on port ${port}`);
});
Routing คือการกำหนดว่าเมื่อ client เรียก URL และ HTTP method ใด server ต้องเรียก function ใดเพื่อประมวลผล เช่น GET /api/products ใช้อ่านรายการสินค้า และ POST /api/products ใช้สร้างสินค้าใหม่
const products = [
{ id: 1, name: 'Keyboard', price: 850 },
{ id: 2, name: 'Mouse', price: 420 }
];
app.get('/api/products', (req, res) => {
res.json(products);
});
app.get('/api/products/:id', (req, res) => {
const id = Number(req.params.id);
const product = products.find((item) => item.id === id);
if (!product) {
return res.status(404).json({
error: { code: 'PRODUCT_NOT_FOUND', message: 'ไม่พบสินค้า' }
});
}
res.json(product);
});
| Method | Route | หน้าที่ |
|---|---|---|
GET |
/api/products |
อ่านรายการสินค้า |
GET |
/api/products/:id |
อ่านสินค้ารายการเดียว |
POST |
/api/products |
เพิ่มสินค้า |
PUT |
/api/products/:id |
แก้ไขสินค้าทั้งรายการ |
PATCH |
/api/products/:id |
แก้ไขบาง field |
DELETE |
/api/products/:id |
ลบสินค้า |
Middleware คือ function ที่ทำงานระหว่างการรับ request และการส่ง response ใช้สำหรับงานที่ต้องทำซ้ำหลาย endpoint เช่น logging, validation, authentication, parsing JSON และ error handling
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#b8bb26',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart LR
A[Request
คำขอ] --> B[Logger
บันทึก]
B --> C[JSON Parser
อ่าน body]
C --> D[Validation
ตรวจข้อมูล]
D --> E[Route Handler
ทำงานหลัก]
E --> F[Response
ผลลัพธ์]
// middleware สำหรับ logging
function requestLogger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next();
}
app.use(requestLogger);
| Middleware | ใช้ทำอะไร |
|---|---|
express.json() |
อ่าน JSON body |
| Logger | บันทึก method และ URL |
| Validation | ตรวจว่าข้อมูลครบและถูกต้อง |
| Authentication | ตรวจ token หรือ session |
| Error Handler | รวมรูปแบบ error response |
ก่อนบันทึกข้อมูลใหม่ server ควรตรวจสอบ request body เพื่อป้องกันข้อมูลไม่ครบหรือผิดชนิด หากข้อมูลผิดควรส่ง status code 400 Bad Request พร้อม error message ที่ front-end นำไปแสดงต่อได้
app.post('/api/products', (req, res) => {
const { name, price } = req.body;
if (!name) {
return res.status(400).json({
error: {
code: 'PRODUCT_NAME_REQUIRED',
message: 'กรุณาระบุชื่อสินค้า',
field: 'name'
}
});
}
if (typeof price !== 'number' || price <= 0) {
return res.status(400).json({
error: {
code: 'PRODUCT_PRICE_INVALID',
message: 'ราคาสินค้าต้องเป็นตัวเลขมากกว่า 0',
field: 'price'
}
});
}
const product = {
id: products.length + 1,
name,
price
};
products.push(product);
res.status(201).json(product);
});
เมื่อระบบใหญ่ขึ้น ไม่ควรเขียน logic ทั้งหมดไว้ใน route handler เพราะจะอ่านยากและทดสอบยาก ควรแบ่งเป็น Controller สำหรับรับ request/response และ Service สำหรับ business logic
// product.service.js
const products = [
{ id: 1, name: 'Keyboard', price: 850 }
];
function findAllProducts() {
return products;
}
function createProduct(data) {
const product = {
id: products.length + 1,
name: data.name,
price: data.price
};
products.push(product);
return product;
}
module.exports = { findAllProducts, createProduct };
// product.controller.js
const productService = require('./product.service');
function getProducts(req, res) {
const products = productService.findAllProducts();
res.json(products);
}
function createProduct(req, res) {
const product = productService.createProduct(req.body);
res.status(201).json(product);
}
module.exports = { getProducts, createProduct };
Error Handling ที่ดีช่วยให้ client เข้าใจปัญหาและช่วยให้นักพัฒนาตรวจสอบระบบได้ง่าย ควรแยกข้อความสำหรับผู้ใช้กับรายละเอียดเชิงเทคนิคออกจากกัน โดยเฉพาะใน production
// middleware สำหรับจัดการ error รวม
function errorHandler(error, req, res, next) {
console.error(error);
res.status(error.status || 500).json({
error: {
code: error.code || 'INTERNAL_SERVER_ERROR',
message: error.message || 'เกิดข้อผิดพลาดในระบบ'
}
});
}
app.use(errorHandler);
{
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "ไม่พบสินค้า",
"field": null
}
}
| Status Code | ใช้เมื่อ |
|---|---|
400 |
request body หรือ parameter ไม่ถูกต้อง |
401 |
ยังไม่ได้ยืนยันตัวตน |
403 |
ไม่มีสิทธิ์เข้าถึง |
404 |
ไม่พบ resource |
409 |
ข้อมูลซ้ำหรือขัดแย้ง |
500 |
server error |
Environment Variables ใช้เก็บค่าตั้งค่าที่ไม่ควร hard-code ใน source code เช่น port, database URL, secret key และ API key ช่วยให้ใช้ config ต่างกันระหว่าง development, testing และ production ได้
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/appdb
JWT_SECRET=change-this-secret
NODE_ENV=development
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
| ตัวแปร | ใช้เก็บ |
|---|---|
PORT |
port ของ server |
DATABASE_URL |
connection string ของฐานข้อมูล |
JWT_SECRET |
secret สำหรับ token |
NODE_ENV |
environment เช่น development/production |
API_BASE_URL |
URL ของบริการอื่นที่ต้องเรียก |
Node-RED เป็นเครื่องมือแบบ flow-based programming ที่ใช้สร้าง HTTP endpoint ได้โดยลาก node มาต่อกัน เหมาะกับงาน prototype, integration และระบบที่ต้องเชื่อมต่อบริการหลายชนิด
%%{init: {'theme': 'base', 'themeVariables': {
'background': '#282828',
'primaryColor': '#3c3836',
'primaryTextColor': '#fbf1c7',
'primaryBorderColor': '#fabd2f',
'lineColor': '#d3869b',
'secondaryColor': '#504945',
'tertiaryColor': '#665c54',
'fontFamily': 'Arial'
}}}%%
flowchart LR
A[HTTP In
รับ endpoint] --> B[Function
เตรียมข้อมูล]
B --> C[Database/API Node
เชื่อมข้อมูล]
C --> D[HTTP Response
ส่งผลลัพธ์]
| Node | หน้าที่ |
|---|---|
| HTTP In | สร้าง endpoint เช่น GET /products |
| Function | เขียน JavaScript เพื่อจัดการข้อมูล |
| Change | เปลี่ยนค่าใน message |
| Database Node | เชื่อมต่อฐานข้อมูล |
| HTTP Response | ส่ง response กลับ client |
เวลาตอบสนองของ API เกิดจากหลายส่วน เช่น network, middleware, business logic และ database query การเข้าใจส่วนประกอบนี้ช่วยให้ debug และปรับ performance ได้ตรงจุด
ในบทนี้ให้สร้าง API สำหรับ resource products โดยใช้ Node.js/Express หรือ Node-RED และทดสอบด้วย Postman
product-api/
server.js
routes/
product.routes.js
controllers/
product.controller.js
services/
product.service.js
middlewares/
error-handler.js
.env
| Endpoint | หน้าที่ |
|---|---|
GET /api/products |
อ่านรายการสินค้า |
GET /api/products/:id |
อ่านสินค้ารายการเดียว |
POST /api/products |
เพิ่มสินค้า |
PUT /api/products/:id |
แก้ไขสินค้า |
DELETE /api/products/:id |
ลบสินค้า |
GET /api/health |
ตรวจสถานะ server |
| รายการตรวจ | คำถามที่ต้องตอบ |
|---|---|
| Routing | มี endpoint สำหรับ resource อย่างน้อย 1 ชุดหรือไม่ |
| Middleware | ใช้ middleware สำหรับ JSON/logging/validation หรือไม่ |
| Validation | ตรวจข้อมูลก่อนสร้างหรือแก้ไขหรือไม่ |
| Error Handling | error response มี status code และ message ชัดเจนหรือไม่ |
| Environment | แยกค่า port หรือ config ด้วย environment variables หรือไม่ |
| Testing | ทดสอบ endpoint ด้วย Postman แล้วหรือไม่ |
| Structure | แยก route/controller/service อ่านง่ายหรือไม่ |
สร้าง API สำหรับระบบ Product API โดยเลือกใช้ Node.js/Express หรือ Node-RED:
GET และ POST เป็นอย่างน้อย