1. วัตถุประสงค์#
บทความนี้จะแนะนำขั้นตอนการออกแบบและพัฒนา RESTful API สำหรับจัดการทรัพยากร ผู้ใช้งาน (Users), สินค้า (Products), คำสั่งซื้อ (Orders) และทรัพยากรแบบซ้อน (Nested Resources) เพื่อแสดงข้อมูลคำสั่งซื้อทั้งหมดของผู้ใช้งาน และสินค้าทั้งหมดที่อยู่ในคำสั่งซื้อ โดยใช้ Node.js และ Express เชื่อมต่อกับฐานข้อมูล MySQL โดยตรงด้วย mysql2 พร้อมทดสอบด้วย Postman
2. ติดตั้งโปรแกรมและตั้งค่าฐานข้อมูล#
-
ติดตั้ง Node.js version LTS ดาวน์โหลด Node.js เวอร์ชัน LTS (Long Term Support) จาก https://nodejs.org ↗ เลือกเวอร์ชัน LTS ที่แนะนำสำหรับการใช้งานในการพัฒนา
การตั้งค่า:
- ดาวน์โหลดไฟล์ installer ที่เหมาะสมกับระบบปฏิบัติการ (Windows, macOS, หรือ Linux)
- ติดตั้งโดยการดับเบิลคลิกไฟล์ installer และปฏิบัติตามขั้นตอนการติดตั้ง
- หลังจากติดตั้งเสร็จสิ้น ให้เปิด Command Prompt หรือ Terminal และตรวจสอบการติดตั้งด้วยคำสั่ง:
bashnode --version npm --version - ถ้าติดตั้งสำเร็จ จะแสดงเวอร์ชันของ Node.js และ npm
-
ติดตั้ง XAMPP (MySQL + phpMyAdmin) ดาวน์โหลดและติดตั้ง XAMPP จาก https://www.apachefriends.org ↗ เลือกเวอร์ชันที่เหมาะสมกับระบบปฏิบัติการ
การตั้งค่า:
- เมื่อติดตั้งเสร็จสิ้น ให้เปิด XAMPP Control Panel และตั้งค่า Apache และ MySQL ให้เป็น Service ดังภาพ (ต้องใช้สิทธิ Administrator)

- Start บริการ Apache และ MySQL เพื่อให้สามารถใช้งานเว็บเซิร์ฟเวอร์และฐานข้อมูลได้
- เปิดเว็บเบราว์เซอร์ไปที่ http://localhost/phpmyadmin ↗
- คลิกที่แท็บ Databases
- ในส่วน Create database ให้ตั้งชื่อฐานข้อมูลว่า express_mysql_db แล้วคลิก Create
- เมื่อติดตั้งเสร็จสิ้น ให้เปิด XAMPP Control Panel และตั้งค่า Apache และ MySQL ให้เป็น Service ดังภาพ (ต้องใช้สิทธิ Administrator)
-
ติดตั้ง Postman ดาวน์โหลด Postman ได้ที่ https://www.postman.com/downloads/ ↗ ติดตั้งและเปิดใช้งาน Postman จะใช้ Postman ในการทดสอบ API endpoints (GET, POST, PUT, DELETE)
3. ตั้งค่าโปรเจคและติดตั้ง MySQL Driver#
-
สร้างโปรเจค Node.js เปิด Terminal หรือ Command Prompt แล้วรันคำสั่งเหล่านี้:
bashmkdir api-mysql cd api-mysql npm init -y npm install express cors body-parser mysql2 code . # เปิด VS Code ในโฟลเดอร์โปรเจกต์ (ถ้าติดตั้ง) -
สร้างไฟล์ .env สร้างไฟล์
.envใน root project เพื่อเก็บค่าการเชื่อมต่อฐานข้อมูล:
plaintextDB_HOST=localhost DB_USER=root DB_PASSWORD= DB_NAME=express_mysql_db DB_PORT=3306DB_HOST: Host ของ MySQL (localhost)DB_USER: ชื่อผู้ใช้ฐานข้อมูล (root สำหรับ XAMPP)DB_PASSWORD: รหัสผ่าน (เว้นว่างไว้สำหรับ XAMPP ถ้าไม่ได้ตั้งค่า)DB_NAME: ชื่อฐานข้อมูลDB_PORT: Port ของ MySQL (3306)
4. สร้างตารางในฐานข้อมูล#
เปิด phpMyAdmin ที่ http://localhost/phpmyadmin ↗ และไปที่ฐานข้อมูล express_mysql_db จากนั้นคลิกที่แท็บ SQL และวางคำสั่ง SQL ต่อไปนี้เพื่อสร้างตาราง:
-- สร้างตาราง Users
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
fname VARCHAR(100) NOT NULL,
lname VARCHAR(100) NOT NULL,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
avatar VARCHAR(255) NULL
);
-- สร้างตาราง Products
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price INT NOT NULL
);
-- สร้างตาราง Orders
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- สร้างตาราง Order_Product (ตารางเชื่อมระหว่าง Orders และ Products)
CREATE TABLE order_product (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
);sqlสรุปโครงสร้างตาราง#
| ตาราง | ความสัมพันธ์ | ประเภท |
|---|---|---|
| users | มีหลายคำสั่งซื้อ (has many orders) | One-to-Many |
| orders | ถูกสร้างโดยผู้ใช้คนเดียว (belongs to one user) | Many-to-One |
| orders | มีหลายสินค้า (has many products) | Many-to-Many |
| products | อยู่ในหลายคำสั่งซื้อ (appears in many orders) | Many-to-Many |
| order_product | เชื่อมตาราง orders และ products | Join table |
ดูผลลัพธ์ได้ที่ฐานข้อมูล express_mysql_db

คำอธิบาย SQL ที่ใช้ในการสร้างตาราง#
CREATE TABLE - คำสั่งสร้างตารางใหม่:
CREATE TABLE table_name (
column_name data_type constraints
);sqlData Types ที่ใช้บ่อย:
INT- ข้อมูลตัวเลขจำนวนเต็มVARCHAR(n)- ข้อมูลตัวอักษรความยาวไม่เกิน n ตัวอักษรTIMESTAMP- ข้อมูลวันที่และเวลาTEXT- ข้อมูลตัวอักษรความยาวไม่จำกัด
Constraints ที่ใช้บ่อย:
PRIMARY KEY- คีย์หลัก ใช้ระบุแต่ละแถวให้ไม่ซ้ำกันAUTO_INCREMENT- เลขที่เพิ่มขึ้นอัตโนมัติ (1, 2, 3, …)NOT NULL- ข้อมูลต้องไม่เป็นค่าว่างUNIQUE- ข้อมูลต้องไม่ซ้ำกันDEFAULT- ค่าเริ่มต้นเมื่อไม่ได้ระบุข้อมูล
ความสัมพันธ์ระหว่างตาราง (Foreign Key):
FOREIGN KEY (column_name) REFERENCES other_table(id)
ON DELETE CASCADE -- ลบข้อมูลที่เกี่ยวข้องเมื่อข้อมูลหลักถูกลบsqlตัวอย่าง:
-- ตาราง users เก็บข้อมูลผู้ใช้
id INT AUTO_INCREMENT PRIMARY KEY -- รหัสผู้ใช้ (เพิ่มอัตโนมัติ)
username VARCHAR(50) NOT NULL UNIQUE -- ชื่อผู้ใช้ (ไม่ซ้ำกัน)
-- ตาราง orders เก็บคำสั่งซื้อ
user_id INT NOT NULL
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
-- user_id เชื่อมโยงกับ id ในตาราง users
-- เมื่อลบ user ข้อมูลคำสั่งซื้อจะถูกลบไปด้วยsql5. เพิ่มข้อมูลตัวอย่าง (Seed Data)#
หลังจากสร้างตารางเสร็จแล้ว เราต้องการข้อมูลตัวอย่างเพื่อทดสอบ API ให้ทำงานได้ทันที ให้เปิด phpMyAdmin ไปที่ฐานข้อมูล express_mysql_db และคลิกที่แท็บ SQL จากนั้นวางคำสั่ง SQL ต่อไปนี้:
-- เพิ่มข้อมูล Users
INSERT INTO users (id, fname, lname, username, email, avatar) VALUES
(1, 'Karn', 'Yong', 'karn.yong', 'karn.yong@melivecode.com', 'https://www.melivecode.com/users/1.png'),
(2, 'Parkpoom', 'Chaisiriprasert', 'parkpoom', 'parkpoom@melivecode.com', 'https://www.melivecode.com/users/2.png');
-- เพิ่มข้อมูล Products
INSERT INTO products (id, name, price) VALUES
(1, 'Laptop', 39999),
(2, 'Smartphone', 19999),
(3, 'Monitor', 7999);
-- เพิ่มข้อมูล Orders
INSERT INTO orders (id, user_id) VALUES
(1, 1),
(2, 2),
(3, 1);
-- เพิ่มข้อมูล Order_Product (เชื่อมโยงคำสั่งซื้อกับสินค้า)
INSERT INTO order_product (order_id, product_id) VALUES
-- คำสั่งซื้อ #1 (Karn) มี Laptop และ Smartphone
(1, 1),
(1, 2),
-- คำสั่งซื้อ #2 (Parkpoom) มี Smartphone
(2, 2),
-- คำสั่งซื้อ #3 (Karn) มี Smartphone และ Monitor
(3, 2),
(3, 3);sqlคำอธิบาย SQL ที่ใช้ในการเพิ่มข้อมูล#
INSERT INTO - คำสั่งเพิ่มข้อมูลใหม่:
INSERT INTO table_name (column1, column2) VALUES (value1, value2);sqlเพิ่มข้อมูลหลายแถวพร้อมกัน:
INSERT INTO users (fname, lname, email) VALUES
('Karn', 'Yong', 'karn@example.com'),
('Parkpoom', 'Chaisiriprasert', 'parkpoom@example.com');sqlข้อมูลตัวอย่างที่เพิ่ม:
| ตาราง | ข้อมูลที่เพิ่ม |
|---|---|
| Users | 2 ผู้ใช้: Karn และ Parkpoom |
| Products | 3 สินค้า: Laptop, Smartphone, Monitor |
| Orders | 3 คำสั่งซื้อ |
| Order_Product | เชื่อมโยงคำสั่งซื้อกับสินค้า |
ความสัมพันธ์ของข้อมูลตัวอย่าง:
- Karn (id: 1) สั่งซื้อ 2 ครั้ง:
- คำสั่งซื้อ #1: Laptop + Smartphone = 59,998 บาท
- คำสั่งซื้อ #3: Smartphone + Monitor = 27,998 บาท
- Parkpoom (id: 2) สั่งซื้อ 1 ครั้ง:
- คำสั่งซื้อ #2: Smartphone = 19,999 บาท
6. สร้างไฟล์เชื่อมต่อฐานข้อมูล#
สร้างไฟล์ src/db.js เพื่อสร้าง connection pool สำหรับเชื่อมต่อกับฐานข้อมูล MySQL:
// src/db.js
const mysql = require('mysql2/promise');
// สร้าง connection pool
const pool = mysql.createPool({
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'express_mysql_db',
port: process.env.DB_PORT || 3306,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool;javascriptคำอธิบาย Connection Pool#
Connection Pool คือการเก็บการเชื่อมต่อฐานข้อมูลไว้ใช้ร่วมกัน เพื่อไม่ต้องเปิด-ปิดการเชื่อมต่อใหม่ทุกครั้ง
7. คำสั่ง SQL พื้นฐานที่ใช้ใน RESTful API#
SELECT - ดึงข้อมูล#
ดึงข้อมูลทั้งหมด:
SELECT * FROM users;sqlดึงข้อมูลตามเงื่อนไข:
SELECT * FROM users WHERE id = 1;sqlดึงข้อมูลบางคอลัมน์:
SELECT fname, lname, email FROM users;sqlINSERT - เพิ่มข้อมูล#
เพิ่มข้อมูลใหม่:
INSERT INTO users (fname, lname, username, email) VALUES ('Anna', 'Dee', 'anna.dee', 'Anna@example.com');sqlใน JavaScript (ใช้ parameter):
const sql = 'INSERT INTO users (fname, lname, username, email) VALUES (?, ?, ?, ?)';
await pool.query(sql, ['Anna', 'Dee', 'anna.dee', 'Anna@example.com']);javascriptUPDATE - อัปเดตข้อมูล#
อัปเดตข้อมูล:
UPDATE users SET fname = 'Jane', email = 'jane@example.com' WHERE id = 3;sqlใน JavaScript:
const sql = 'UPDATE users SET fname = ?, email = ? WHERE id = ?';
await pool.query(sql, ['Jane', 'jane@example.com', 1]);javascriptDELETE - ลบข้อมูล#
ลบข้อมูล:
DELETE FROM users WHERE id = 3;sqlใน JavaScript:
await pool.query('DELETE FROM users WHERE id = ?', [3]);javascriptJOIN - เชื่อมตาราง#
LEFT JOIN - เชื่อมตารางและแสดงข้อมูลฝั่งซ้ายทั้งหมด:
SELECT users.fname, orders.id
FROM users
LEFT JOIN orders ON users.id = orders.user_id;sqlใช้ JOIN หลายตาราง:
SELECT o.id, u.fname, p.name, p.price
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_product op ON o.id = op.order_id
JOIN products p ON op.product_id = p.id;sqlParameterized Queries (?)#
ทำไมต้องใช้ ??
- ปลอดภัย - ป้องกัน SQL Injection
- สะอาด - โค้ดอ่านง่ายขึ้น
- ถูกต้อง - จัดการ data types ให้อัตโนมัติ
ไม่ดี (เสี่ยง SQL Injection):
const sql = `SELECT * FROM users WHERE id = ${userId}`;
// ถ้า userId = "1; DROP TABLE users;" จะเกิดอันตรายjavascriptดี (ปลอดภัย):
const sql = 'SELECT * FROM users WHERE id = ?';
await pool.query(sql, [userId]);
// ตัว ? จะถูกแทนที่ด้วยค่าที่ปลอดภัยjavascript8. สร้าง index.js พร้อม Express + MySQL Endpoints#
สร้างไฟล์ index.js ใน root project และเพิ่มโค้ดพื้นฐานของ Express server พร้อมกับ database connection ที่เราสร้างไว้:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const pool = require('./src/db'); // Import database connection
const app = express();
const port = 5000; // Define port
// Middleware
app.use(cors());
app.use(bodyParser.json());
// Test route
app.get('/', (req, res) => {
res.send('Hello! RESTful API is ready to use with MySQL');
});
// Add API endpoints below this line
// Start server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});javascriptเริ่มต้นและทดสอบ API Server:
node index.jsbashเปิดเว็บเบราว์เซอร์ไปที่ http://localhost:5000 ↗ จะเห็นข้อความ “Hello! RESTful API is ready to use with MySQL” ดังรูป

ต่อไปจะเป็นการเพิ่ม endpoints สำหรับ User, Product และ Order โดยใช้ SQL queries ในการจัดการข้อมูล
8.1. User Endpoints#
เพิ่มโค้ดต่อไปนี้ในไฟล์ index.js ใต้ app.get('/')
// User Endpoints
// Read All Users
app.get('/users', async (req, res) => {
try {
const [rows] = await pool.query('SELECT * FROM users');
res.json(rows);
} catch (error) {
res.status(500).json({ message: 'Error fetching users', error: error.message });
}
});
// Read One User
app.get('/users/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
if (rows.length === 0) {
return res.status(404).json({ message: 'User not found' });
}
res.json(rows[0]);
} catch (error) {
res.status(500).json({ message: 'Error fetching user', error: error.message });
}
});
// Create User
app.post('/users', async (req, res) => {
try {
const { fname, lname, username, email, avatar } = req.body;
const sql = 'INSERT INTO users (fname, lname, username, email, avatar) VALUES (?, ?, ?, ?, ?)';
const [result] = await pool.query(sql, [fname, lname, username, email, avatar || null]);
const [newUser] = await pool.query('SELECT * FROM users WHERE id = ?', [result.insertId]);
res.status(201).json(newUser[0]);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(409).json({ message: 'Username or email already exists' });
}
res.status(500).json({ message: 'Error creating user', error: error.message });
}
});
// Update User
app.put('/users/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
const { fname, lname, username, email, avatar } = req.body;
// Check if user exists
const [existing] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
if (existing.length === 0) {
return res.status(404).json({ message: 'User not found' });
}
const sql = 'UPDATE users SET fname = ?, lname = ?, username = ?, email = ?, avatar = ? WHERE id = ?';
await pool.query(sql, [fname, lname, username, email, avatar || null, id]);
const [updated] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
res.status(200).json(updated[0]);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(409).json({ message: 'Username or email already exists' });
}
res.status(500).json({ message: 'Error updating user', error: error.message });
}
});
// Delete User
app.delete('/users/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
// Check if user exists
const [existing] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
if (existing.length === 0) {
return res.status(404).json({ message: 'User not found' });
}
await pool.query('DELETE FROM users WHERE id = ?', [id]);
res.status(200).json({ message: `User with ID ${id} deleted successfully` });
} catch (error) {
res.status(500).json({ message: 'Error deleting user', error: error.message });
}
});javascriptคำอธิบาย SQL ใน User Endpoints:
-
SELECT * FROM users
- ดึงข้อมูลทุกคอลัมน์จากตาราง users
- ผลลัพธ์: รายการ users ทั้งหมด
-
SELECT * FROM users WHERE id = ?
- ดึงข้อมูล user ที่มี id ตรงกับค่าที่ระบุ
?คือ placeholder ที่จะถูกแทนที่ด้วยค่าจาก array[id]- ผลลัพธ์: user 1 คน หรือ empty array ถ้าไม่พบ
-
INSERT INTO users (…) VALUES (?, ?, ?, ?, ?)
- เพิ่มข้อมูล user ใหม่
result.insertIdคือ id ที่ถูกสร้างอัตโนมัติ- หลังจาก INSERT ให้ SELECT อีกครั้งเพื่อดึงข้อมูลที่เพิ่งสร้าง
-
UPDATE users SET … WHERE id = ?
- อัปเดตข้อมูล user ที่มี id ตรงกับค่าที่ระบุ
- หลังจาก UPDATE ให้ SELECT เพื่อดึงข้อมูลที่อัปเดตแล้ว
-
DELETE FROM users WHERE id = ?
- ลบข้อมูล user ที่มี id ตรงกับค่าที่ระบุ
- ข้อมูลในตาราง orders จะถูกลบด้วย (CASCADE)
Error Handling:
ER_DUP_ENTRY- เกิดเมื่อข้อมูลซ้ำ (username หรือ email ซ้ำ)rows.length === 0- ไม่พบข้อมูล
| HTTP Method | URI | วัตถุประสงค์ | Logic | Response |
|---|---|---|---|---|
| GET | /users | ดึงข้อมูลผู้ใช้ทั้งหมด | ใช้ SELECT * FROM users เพื่อดึงข้อมูลทั้งหมด | รายการของ Object ผู้ใช้ทั้งหมด |
| GET | /users/:id | ดึงข้อมูลผู้ใช้ตาม ID | ใช้ SELECT WHERE id = ? จาก URL parameter id หากไม่พบจะคืนค่า 404 | Object ผู้ใช้ หรือ 404 (ไม่พบ) |
| POST | /users | สร้างผู้ใช้ใหม่ | req.body ควรมี fname, lname, username, email, avatar ใช้ INSERT INTO users | Object ผู้ใช้ที่สร้างใหม่ หรือ 409 (ซ้ำ) หรือ 500 (ข้อผิดพลาด) |
| PUT | /users/:id | อัปเดตผู้ใช้ตาม ID | อัปเดตข้อมูลผู้ใช้โดยใช้ ID จาก URL และข้อมูลจาก Body จัดการข้อผิดพลาด (เช่น ไม่พบผู้ใช้ หรือข้อมูลซ้ำ) | Object ผู้ใช้ที่อัปเดต หรือ 404 (ไม่พบ) หรือ 409 (ซ้ำ) หรือ 500 (ข้อผิดพลาด) |
| DELETE | /users/:id | ลบผู้ใช้ตาม ID | ใช้ DELETE FROM users WHERE id = ? ด้วย ID ที่กำหนด | ข้อความยืนยันการลบ หรือ 404 (ไม่พบ) หรือ 500 (ข้อผิดพลาด) |
ตัวอย่างการทดสอบ API POST /users
วัตถุประสงค์ สร้างผู้ใช้ใหม่
Logic
- req.body ต้องประกอบด้วยข้อมูลต่อไปนี้: fname (ชื่อจริง), lname (นามสกุล), username (ชื่อผู้ใช้), email (อีเมล), และ avatar (รูปโปรไฟล์)
- ระบบจะใช้คำสั่ง INSERT INTO users เพื่อบันทึกข้อมูลผู้ใช้ลงในฐานข้อมูล
Response
อ็อบเจกต์ของข้อมูลผู้ใช้ที่ถูกสร้างขึ้นมาใหม่

ตัวอย่างการทดสอบ API GET /users
วัตถุประสงค์ ดึงข้อมูลผู้ใช้ทั้งหมด
Logic
- ระบบจะใช้คำสั่ง SELECT * FROM users เพื่อเรียกดูข้อมูลผู้ใช้ทั้งหมดจากฐานข้อมูล
Response
รายการอ็อบเจกต์ของข้อมูลผู้ใช้ทั้งหมด

ตัวอย่างการทดสอบ API GET /users/:id
วัตถุประสงค์ ดึงข้อมูลผู้ใช้รายเดียวด้วย ID
Logic
- ระบบจะใช้คำสั่ง SELECT WHERE id = ? โดยใช้ค่า id ที่ได้จากพารามิเตอร์ใน URL
- หากไม่พบผู้ใช้ ระบบจะส่งคืนสถานะ 404 (Not Found)
Response อ็อบเจกต์ของข้อมูลผู้ใช้เพียงรายเดียว
ตัวอย่างการทดสอบ API GET /users/:id

ตัวอย่างการทดสอบ API GET /users/:id แบบไม่พบ user

ตัวอย่างการทดสอบ API PUT /users/:id
วัตถุประสงค์ อัปเดตข้อมูลผู้ใช้ด้วย ID
Logic
- ระบบจะแยกวิเคราะห์ (parse) ค่า id จาก URL
- ใช้คำสั่ง UPDATE users SET … WHERE id = ? พร้อมข้อมูลใหม่ที่ต้องการอัปเดต
- มีการดักจับข้อผิดพลาด (error handling) เช่น กรณีที่ไม่พบผู้ใช้
Response อ็อบเจกต์ของข้อมูลผู้ใช้ที่ได้รับการอัปเดตแล้ว

ตัวอย่างการทดสอบ API DELETE /users/:id
วัตถุประสงค์ ลบผู้ใช้ด้วย ID
Logic
- ระบบจะใช้คำสั่ง DELETE FROM users WHERE id = ? โดยใช้ค่า ID ที่ระบุ
Response ข้อความยืนยันการลบผู้ใช้

8.2. Product Endpoints#
เพิ่มโค้ดต่อไปนี้ในไฟล์ index.js
// Product Endpoints
// Read All Products
app.get('/products', async (req, res) => {
try {
const [rows] = await pool.query('SELECT * FROM products');
res.json(rows);
} catch (error) {
res.status(500).json({ message: 'Error fetching products', error: error.message });
}
});
// Read One Product
app.get('/products/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
const [rows] = await pool.query('SELECT * FROM products WHERE id = ?', [id]);
if (rows.length === 0) {
return res.status(404).json({ message: 'Product not found' });
}
res.json(rows[0]);
} catch (error) {
res.status(500).json({ message: 'Error fetching product', error: error.message });
}
});
// Create Product
app.post('/products', async (req, res) => {
try {
const { name, price } = req.body;
const sql = 'INSERT INTO products (name, price) VALUES (?, ?)';
const [result] = await pool.query(sql, [name, price]);
const [newProduct] = await pool.query('SELECT * FROM products WHERE id = ?', [result.insertId]);
res.status(201).json(newProduct[0]);
} catch (error) {
res.status(500).json({ message: 'Error creating product', error: error.message });
}
});
// Update Product
app.put('/products/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
const { name, price } = req.body;
// Check if product exists
const [existing] = await pool.query('SELECT * FROM products WHERE id = ?', [id]);
if (existing.length === 0) {
return res.status(404).json({ message: 'Product not found' });
}
const sql = 'UPDATE products SET name = ?, price = ? WHERE id = ?';
await pool.query(sql, [name, price, id]);
const [updated] = await pool.query('SELECT * FROM products WHERE id = ?', [id]);
res.status(200).json(updated[0]);
} catch (error) {
res.status(500).json({ message: 'Error updating product', error: error.message });
}
});
// Delete Product
app.delete('/products/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
// Check if product exists
const [existing] = await pool.query('SELECT * FROM products WHERE id = ?', [id]);
if (existing.length === 0) {
return res.status(404).json({ message: 'Product not found' });
}
await pool.query('DELETE FROM products WHERE id = ?', [id]);
res.status(200).json({ message: `Product with ID ${id} deleted successfully` });
} catch (error) {
res.status(500).json({ message: 'Error deleting product', error: error.message });
}
});javascript| HTTP Method | URI | วัตถุประสงค์ | Logic | Response |
|---|---|---|---|---|
| GET | /products | ดึงข้อมูลสินค้าทั้งหมด | ใช้ SELECT * FROM products เพื่อดึงข้อมูลทั้งหมด | รายการของ Object สินค้าทั้งหมด |
| GET | /products/:id | ดึงข้อมูลสินค้าตาม ID | ใช้ SELECT WHERE id = ? จาก URL parameter id หากไม่พบจะคืนค่า 404 | Object สินค้า หรือ 404 (ไม่พบ) |
| POST | /products | สร้างสินค้าใหม่ | req.body ควรมีข้อมูลสินค้า ใช้ INSERT INTO products | Object สินค้าที่สร้างใหม่ หรือ 500 (ข้อผิดพลาด) |
| PUT | /products/:id | อัปเดตสินค้าตาม ID | อัปเดตข้อมูลสินค้าโดยใช้ ID จาก URL และข้อมูลจาก Body จัดการข้อผิดพลาด (เช่น ไม่พบสินค้า) | Object สินค้าที่อัปเดต หรือ 404 (ไม่พบ) หรือ 500 (ข้อผิดพลาด) |
| DELETE | /products/:id | ลบสินค้าตาม ID | ใช้ DELETE FROM products WHERE id = ? ด้วย ID ที่กำหนด | ข้อความยืนยันการลบ หรือ 404 (ไม่พบ) หรือ 500 (ข้อผิดพลาด) |
ตัวอย่างการทดสอบ API POST /products
วัตถุประสงค์ สร้างสินค้าใหม่
Logic
- req.body ต้องประกอบด้วยข้อมูลสินค้า
- ระบบจะใช้คำสั่ง INSERT INTO products เพื่อบันทึกข้อมูลสินค้าลงในฐานข้อมูล
Response อ็อบเจกต์ของข้อมูลสินค้าที่ถูกสร้างขึ้นมาใหม่

ตัวอย่างการทดสอบ API GET /products
วัตถุประสงค์ ดึงข้อมูลสินค้าทั้งหมด
Logic
- ระบบจะใช้คำสั่ง SELECT * FROM products เพื่อเรียกดูข้อมูลสินค้าทั้งหมดจากฐานข้อมูล
Response รายการอ็อบเจกต์ของข้อมูลสินค้าทั้งหมด

ตัวอย่างการทดสอบ API GET /products/:id
วัตถุประสงค์ ดึงข้อมูลสินค้ารายเดียวด้วย ID
Logic
- ระบบจะใช้คำสั่ง SELECT WHERE id = ? โดยใช้ค่า id ที่ได้จากพารามิเตอร์ใน URL
- หากไม่พบสินค้า ระบบจะส่งคืนสถานะ 404 (Not Found)
Response อ็อบเจกต์ของข้อมูลสินค้าเพียงรายเดียว

ตัวอย่างการทดสอบ API PUT /products/:id
วัตถุประสงค์ อัปเดตข้อมูลสินค้าด้วย ID
Logic
- ระบบจะแยกวิเคราะห์ (parse) ค่า id จาก URL
- ใช้คำสั่ง UPDATE products SET … WHERE id = ? พร้อมข้อมูลใหม่ที่ต้องการอัปเดต
- มีการดักจับข้อผิดพลาด (error handling) เช่น กรณีที่ไม่พบสินค้า
Response อ็อบเจกต์ของข้อมูลสินค้าที่ได้รับการอัปเดตแล้ว

ตัวอย่างการทดสอบ API DELETE /products/:id
วัตถุประสงค์ ลบสินค้าด้วย ID
Logic
- ระบบจะใช้คำสั่ง DELETE FROM products WHERE id = ? โดยใช้ค่า ID ที่ระบุ
Response ข้อความยืนยันการลบสินค้า

8.3. Order Endpoints#
เพิ่มโค้ดต่อไปนี้ในไฟล์ index.js
// Order Endpoints
// Read All Orders
app.get('/orders', async (req, res) => {
try {
const [orders] = await pool.query(`
SELECT
o.id,
o.user_id,
o.created_at,
u.id as user_id,
u.fname,
u.lname,
u.username,
u.email,
u.avatar
FROM orders o
JOIN users u ON o.user_id = u.id
`);
// Get products for each order
for (const order of orders) {
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [order.id]);
order.products = products;
order.user = {
id: order.user_id,
fname: order.fname,
lname: order.lname,
username: order.username,
email: order.email,
avatar: order.avatar
};
delete order.fname;
delete order.lname;
delete order.username;
delete order.email;
delete order.avatar;
}
res.json(orders);
} catch (error) {
res.status(500).json({ message: 'Error fetching orders', error: error.message });
}
});
// Read One Order
app.get('/orders/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
const [orders] = await pool.query(`
SELECT
o.id,
o.user_id,
o.created_at,
u.fname,
u.lname,
u.username,
u.email,
u.avatar
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.id = ?
`, [id]);
if (orders.length === 0) {
return res.status(404).json({ message: 'Order not found' });
}
const order = orders[0];
// Get products for the order
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [id]);
order.products = products;
order.user = {
id: order.user_id,
fname: order.fname,
lname: order.lname,
username: order.username,
email: order.email,
avatar: order.avatar
};
delete order.fname;
delete order.lname;
delete order.username;
delete order.email;
delete order.avatar;
res.json(order);
} catch (error) {
res.status(500).json({ message: 'Error fetching order', error: error.message });
}
});
// Create Order
app.post('/orders', async (req, res) => {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
const { userId, productIds } = req.body;
// Check if user exists
const [users] = await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
if (users.length === 0) {
await connection.rollback();
return res.status(400).json({ message: 'Invalid user ID' });
}
// Create order
const [orderResult] = await connection.query('INSERT INTO orders (user_id) VALUES (?)', [userId]);
const orderId = orderResult.insertId;
// Add products to order
for (const productId of productIds) {
await connection.query('INSERT INTO order_product (order_id, product_id) VALUES (?, ?)', [orderId, productId]);
}
await connection.commit();
// Get complete order data
const [orders] = await pool.query(`
SELECT
o.id,
o.user_id,
o.created_at,
u.fname,
u.lname,
u.username,
u.email,
u.avatar
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.id = ?
`, [orderId]);
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [orderId]);
const order = orders[0];
order.products = products;
order.user = {
id: order.user_id,
fname: order.fname,
lname: order.lname,
username: order.username,
email: order.email,
avatar: order.avatar
};
delete order.fname;
delete order.lname;
delete order.username;
delete order.email;
delete order.avatar;
res.status(201).json(order);
} catch (error) {
await connection.rollback();
if (error.code === 'ER_NO_REFERENCED_ROW_2') {
return res.status(400).json({ message: 'Invalid user ID or product ID' });
}
res.status(500).json({ message: 'Error creating order', error: error.message });
} finally {
connection.release();
}
});
// Update Order
app.put('/orders/:id', async (req, res) => {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
const id = parseInt(req.params.id);
const { userId, productIds } = req.body;
// Check if order exists
const [existing] = await connection.query('SELECT * FROM orders WHERE id = ?', [id]);
if (existing.length === 0) {
await connection.rollback();
return res.status(404).json({ message: 'Order not found' });
}
// Update order user
await connection.query('UPDATE orders SET user_id = ? WHERE id = ?', [userId, id]);
// Delete old products and add new ones
await connection.query('DELETE FROM order_product WHERE order_id = ?', [id]);
for (const productId of productIds) {
await connection.query('INSERT INTO order_product (order_id, product_id) VALUES (?, ?)', [id, productId]);
}
await connection.commit();
// Get complete order data
const [orders] = await pool.query(`
SELECT
o.id,
o.user_id,
o.created_at,
u.fname,
u.lname,
u.username,
u.email,
u.avatar
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.id = ?
`, [id]);
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [id]);
const order = orders[0];
order.products = products;
order.user = {
id: order.user_id,
fname: order.fname,
lname: order.lname,
username: order.username,
email: order.email,
avatar: order.avatar
};
delete order.fname;
delete order.lname;
delete order.username;
delete order.email;
delete order.avatar;
res.status(200).json(order);
} catch (error) {
await connection.rollback();
if (error.code === 'ER_NO_REFERENCED_ROW_2') {
return res.status(400).json({ message: 'Invalid user ID or product ID' });
}
res.status(500).json({ message: 'Error updating order', error: error.message });
} finally {
connection.release();
}
});
// Delete Order
app.delete('/orders/:id', async (req, res) => {
try {
const id = parseInt(req.params.id);
// Check if order exists
const [existing] = await pool.query('SELECT * FROM orders WHERE id = ?', [id]);
if (existing.length === 0) {
return res.status(404).json({ message: 'Order not found' });
}
// Delete related order_product records (will be auto-deleted by CASCADE)
await pool.query('DELETE FROM orders WHERE id = ?', [id]);
res.status(200).json({ message: `Order with ID ${id} deleted successfully` });
} catch (error) {
res.status(500).json({ message: 'Error deleting order', error: error.message });
}
});javascriptคำอธิบาย SQL JOIN ใน Order Endpoints:
1. JOIN เพื่อดึงข้อมูลคำสั่งซื้อพร้อมผู้ใช้:
SELECT
o.id, o.user_id, o.created_at,
u.fname, u.lname, u.username, u.email, u.avatar
FROM orders o
JOIN users u ON o.user_id = u.idsqloและuคือ alias (ชื่อย่อ) ของตารางJOIN users u ON o.user_id = u.idเชื่อมตาราง orders กับ users- ผลลัพธ์: คำสั่งซื้อพร้อมข้อมูลผู้ใช้
2. JOIN เพื่อดึงสินค้าในคำสั่งซื้อ:
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?sql- เชื่อมตาราง products กับ order_product
- กรองเฉพาะสินค้าที่อยู่ในคำสั่งซื้อที่ระบุ
3. Transaction สำหรับ CREATE/UPDATE:
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// หลายคำสั่ง SQL
await connection.commit();
} catch (error) {
await connection.rollback();
}javascript- Transaction ใช้เมื่อต้องทำหลายคำสั่งให้สำเร็จทั้งหมด
- commit - ยืนยันการเปลี่ยนแปลง
- rollback - ยกเลิกการเปลี่ยนแปลงถ้าเกิดข้อผิดพลาด
4. INSERT พร้อมการเชื่อมโยง:
INSERT INTO orders (user_id) VALUES (?); -- สร้างคำสั่งซื้อ
INSERT INTO order_product (order_id, product_id) -- เชื่อมสินค้า
VALUES (?, ?);sql5. DELETE พร้อม CASCADE:
DELETE FROM orders WHERE id = ?
-- ข้อมูลใน order_product จะถูกลบอัตโนมัติ (CASCADE)sql| HTTP Method | URI | วัตถุประสงค์ | Logic | Response |
|---|---|---|---|---|
| GET | /orders | ดึงข้อมูลคำสั่งซื้อทั้งหมด | ใช้ JOIN เพื่อดึงข้อมูลคำสั่งซื้อพร้อมผู้ใช้และสินค้า | รายการของ Object คำสั่งซื้อทั้งหมดพร้อมข้อมูลผู้ใช้และสินค้า |
| GET | /orders/:id | ดึงข้อมูลคำสั่งซื้อตาม ID | ใช้ JOIN WHERE id = ? พร้อมข้อมูลสินค้า หากไม่พบจะคืนค่า 404 | Object คำสั่งซื้อพร้อมข้อมูลผู้ใช้และสินค้า หรือ 404 (ไม่พบ) |
| POST | /orders | สร้างคำสั่งซื้อใหม่ | req.body ต้องมี userId และ productIds ใช้ Transaction และ INSERT พร้อมสร้างความสัมพันธ์กับสินค้า | Object คำสั่งซื้อที่สร้างใหม่พร้อมข้อมูลผู้ใช้และสินค้า หรือ 400 (ข้อมูลไม่ถูกต้อง) หรือ 500 (ข้อผิดพลาด) |
| PUT | /orders/:id | อัปเดตคำสั่งซื้อตาม ID | อัปเดตข้อมูลคำสั่งซื้อ ลบสินค้าเก่าและเพิ่มสินค้าใหม่ จัดการข้อผิดพลาด | Object คำสั่งซื้อที่อัปเดตพร้อมข้อมูลผู้ใช้และสินค้า หรือ 404 (ไม่พบ) หรือ 400 (ข้อมูลไม่ถูกต้อง) หรือ 500 (ข้อผิดพลาด) |
| DELETE | /orders/:id | ลบคำสั่งซื้อตาม ID | ลบคำสั่งซื้อ (ข้อมูลใน order_product จะถูกลบโดย CASCADE) | ข้อความยืนยันการลบ หรือ 404 (ไม่พบ) หรือ 500 (ข้อผิดพลาด) |
ตัวอย่างการทดสอบ API POST /orders
วัตถุประสงค์ สร้างคำสั่งซื้อใหม่
Logic
- req.body ต้องประกอบด้วยข้อมูลต่อไปนี้: userId (รหัสผู้ใช้) และ productIds (รายการรหัสสินค้า)
- ระบบจะใช้คำสั่ง INSERT INTO orders และ INSERT INTO order_product พร้อม Transaction เพื่อสร้างคำสั่งซื้อและเชื่อมโยงกับสินค้าที่เลือก
- ข้อมูลจะถูกส่งกลับพร้อมข้อมูลผู้ใช้และรายละเอียดสินค้า
Response อ็อบเจกต์ของข้อมูลคำสั่งซื้อที่ถูกสร้างขึ้นมาใหม่พร้อมข้อมูลผู้ใช้และสินค้า

ตัวอย่างการทดสอบ API GET /orders
วัตถุประสงค์ ดึงข้อมูลคำสั่งซื้อทั้งหมด
Logic
- ระบบจะใช้คำสั่ง JOIN เพื่อดึงข้อมูลคำสั่งซื้อทั้งหมดพร้อมข้อมูลผู้ใช้และสินค้า
Response รายการอ็อบเจกต์ของข้อมูลคำสั่งซื้อทั้งหมดพร้อมข้อมูลผู้ใช้และสินค้า

ตัวอย่างการทดสอบ API GET /orders/:id
วัตถุประสงค์ ดึงข้อมูลคำสั่งซื้อรายเดียวด้วย ID
Logic
- ระบบจะใช้คำสั่ง JOIN WHERE id = ? โดยใช้ค่า id ที่ได้จากพารามิเตอร์ใน URL
- ใช้ JOIN เพื่อดึงข้อมูลผู้ใช้และสินค้าที่เกี่ยวข้อง
- หากไม่พบคำสั่งซื้อ ระบบจะส่งคืนสถานะ 404 (Not Found)
Response อ็อบเจกต์ของข้อมูลคำสั่งซื้อเพียงรายเดียวพร้อมข้อมูลผู้ใช้และสินค้า

ตัวอย่างการทดสอบ API PUT /orders/:id
วัตถุประสงค์ อัปเดตข้อมูลคำสั่งซื้อด้วย ID
Logic
- ระบบจะแยกวิเคราะห์ (parse) ค่า id จาก URL
- ใช้คำสั่ง UPDATE orders และ DELETE/INSERT order_product พร้อมข้อมูลใหม่ใน Transaction
- ลบความสัมพันธ์กับสินค้าเก่าทั้งหมด และสร้างความสัมพันธ์ใหม่กับสินค้าที่เลือก
- มีการดักจับข้อผิดพลาด (error handling) เช่น กรณีที่ไม่พบคำสั่งซื้อ
Response อ็อบเจกต์ของข้อมูลคำสั่งซื้อที่ได้รับการอัปเดตแล้วพร้อมข้อมูลผู้ใช้และสินค้า

ตัวอย่างการทดสอบ API DELETE /orders/:id
วัตถุประสงค์ ลบคำสั่งซื้อด้วย ID
Logic
- ระบบจะใช้คำสั่ง DELETE FROM orders WHERE id = ?
- ข้อมูลในตาราง order_product จะถูกลบอัตโนมัติโดย CASCADE
Response ข้อความยืนยันการลบคำสั่งซื้อ
|
8.4. Nested Endpoints#
เพิ่มโค้ดต่อไปนี้ในไฟล์ index.js เพื่อสร้าง endpoints สำหรับทรัพยากรแบบซ้อน
// Relationship Endpoints
// Get Orders by User ID
app.get('/users/:id/orders', async (req, res) => {
const id = parseInt(req.params.id);
try {
// Check if user exists
const [users] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
if (users.length === 0) {
return res.status(404).json({ message: 'User not found' });
}
const [orders] = await pool.query(`
SELECT
o.id,
o.user_id,
o.created_at,
u.fname,
u.lname,
u.username,
u.email,
u.avatar
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.user_id = ?
`, [id]);
// Get products for each order
for (const order of orders) {
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [order.id]);
order.products = products;
order.user = {
id: order.user_id,
fname: order.fname,
lname: order.lname,
username: order.username,
email: order.email,
avatar: order.avatar
};
delete order.fname;
delete order.lname;
delete order.username;
delete order.email;
delete order.avatar;
}
res.json(orders);
} catch (error) {
res.status(500).json({ message: 'Error fetching user orders', error: error.message });
}
});
// Get Products by Order ID
app.get('/orders/:id/products', async (req, res) => {
const id = parseInt(req.params.id);
try {
// Check if order exists
const [orders] = await pool.query('SELECT * FROM orders WHERE id = ?', [id]);
if (orders.length === 0) {
return res.status(404).json({ message: 'Order not found' });
}
const [products] = await pool.query(`
SELECT p.*
FROM products p
JOIN order_product op ON p.id = op.product_id
WHERE op.order_id = ?
`, [id]);
res.json(products);
} catch (error) {
res.status(500).json({ message: 'Error fetching order products', error: error.message });
}
});javascript| HTTP Method | URI | วัตถุประสงค์ | Logic | Response |
|---|---|---|---|---|
GET | /users/:id/orders | ดึงคำสั่งซื้อทั้งหมดของผู้ใช้ | ตรวจสอบว่าผู้ใช้มีอยู่จริง จากนั้นใช้ JOIN WHERE user_id = ? | รายการ Object คำสั่งซื้อของผู้ใช้นั้นๆ พร้อมข้อมูลสินค้า หรือ 404 (ไม่พบผู้ใช้) หรือ 500 (ข้อผิดพลาด) |
GET | /orders/:id/products | ดึงสินค้าทั้งหมดในคำสั่งซื้อ | ใช้ JOIN เพื่อหาคำสั่งซื้อพร้อมสินค้า จากนั้นคืนค่าเฉพาะข้อมูลสินค้า | รายการ Object สินค้าในคำสั่งซื้อนั้นๆ หรือ 404 (ไม่พบคำสั่งซื้อ) หรือ 500 (ข้อผิดพลาด) |
ตัวอย่างการทดสอบ API GET /users/:id/orders
วัตถุประสงค์ ดึงข้อมูลคำสั่งซื้อทั้งหมดของผู้ใช้คนหนึ่ง
Logic
- ระบบจะตรวจสอบว่าผู้ใช้ที่มี id ตามที่ระบุมีอยู่ในฐานข้อมูลหรือไม่
- หากพบผู้ใช้ ระบบจะใช้คำสั่ง JOIN WHERE user_id = ? โดยกรองด้วย user_id
- ใช้ JOIN เพื่อดึงข้อมูลสินค้าและผู้ใช้ที่เกี่ยวข้อง
- หากไม่พบผู้ใช้ ระบบจะส่งคืนสถานะ 404 (User not found)
Response รายการอ็อบเจกต์ของคำสั่งซื้อทั้งหมดของผู้ใช้นั้นๆ พร้อมข้อมูลสินค้า

ตัวอย่างการทดสอบ API GET /orders/:id/products
วัตถุประสงค์ ดึงข้อมูลสินค้าทั้งหมดในคำสั่งซื้อหนึ่งๆ
Logic
- ระบบจะใช้คำสั่ง JOIN โดยใช้ค่า id ของคำสั่งซื้อที่ได้จากพารามิเตอร์ใน URL
- ใช้ JOIN เพื่อดึงข้อมูลสินค้าที่เกี่ยวข้องกับคำสั่งซื้อ
- หากไม่พบคำสั่งซื้อ ระบบจะส่งคืนสถานะ 404 (Order not found)
Response รายการอ็อบเจกต์ของสินค้าทั้งหมดที่อยู่ในคำสั่งซื้อนั้นๆ

หมายเหตุเพิ่มเติม
Relationship Endpoints เหล่านี้ช่วยให้การเข้าถึงข้อมูลที่เกี่ยวข้องกันมีความสะดวกมากขึ้น:
- /users/:id/orders - เหมาะสำหรับการแสดงประวัติการสั่งซื้อของลูกค้าแต่ละคน
- /orders/:id/products - เหมาะสำหรับการแสดงรายการสินค้าในใบสั่งซื้อเฉพาะ
ทั้งสอง endpoints นี้ช่วยลดความซับซ้อนในการเรียกใช้ข้อมูลจากฝั่ง frontend และทำให้ API มีความยืดหยุ่นในการใช้งานมากขึ้น
การทำความเข้าใจโครงสร้าง API และการเขียน SQL queries โดยตรงจะช่วยให้เข้าใจหลักการพัฒนา Web Application และการจัดการฐานข้อมูลได้ดีมากขึ้นครับ