เอกสารประกอบการศึกษาสำหรับไฟล์ server-api.js
ไฟล์ server-api.js เป็นตัวอย่างการสร้าง REST API Server อย่างง่ายโดยใช้ Node.js และ Express.js ซึ่งเป็น Web Framework ที่นิยมใช้ในการสร้าง API สำหรับการพัฒนา Backend Application
REST API (Representational State Transfer Application Programming Interface) เป็นรูปแบบการออกแบบสถาปัตยกรรมสำหรับการสื่อสารระหว่างระบบต่างๆ ผ่านโปรโตคอล HTTP โดยใช้ HTTP Methods เช่น GET, POST, PUT, DELETE ในการดำเนินการต่างๆ กับข้อมูล
เมื่อศึกษาเอกสารฉบับนี้แล้ว นักเรียนจะสามารถ:
เปิด Command Prompt หรือ Terminal แล้วพิมพ์คำสั่งดังนี้:
# สร้างโปรเจคใหม่
npm init -y
# ติดตั้ง Express
npm install express
คำอธิบาย:
npm init -y = สร้างไฟล์ package.json สำหรับจัดการ dependencies ของโปรเจคnpm install express = ติดตั้ง Express.js framework เข้าสู่โปรเจคconst express = require('express');
const app = express();
const PORT = 3000;
คำอธิบายแต่ละบรรทัด:
require('express') = นำเข้าโมดูล Express.js เข้ามาใช้งานในไฟล์app = express() = สร้าง instance ของแอพพลิเคชัน Express ใหม่PORT = 3000 = กำหนดหมายเลขพอร์ตที่เซิร์ฟเวอร์จะทำงาน (สามารถเปลี่ยนเป็นเลขอื่นได้)app.use(express.json());
คำอธิบาย:
express.json() = แปลงข้อมูล JSON ที่ส่งมาจาก Client ให้เป็น JavaScript Object ที่สามารถนำมาใช้งานได้let items = [
{ id: 1, name: 'Item 1', description: 'First item' },
{ id: 2, name: 'Item 2', description: 'Second item' }
];
คำอธิบาย:
id = รหัสประจำตัวของ item (ไม่ซ้ำกัน)name = ชื่อของ itemdescription = คำอธิบาย itemหมายเหตุสำคัญ:
app.get('/api/items', (req, res) => {
res.json(items);
});
คำอธิบายแต่ละส่วน:
app.get() = กำหนด Route สำหรับ HTTP GET Request'/api/items' = URL Path ที่ Client จะใช้เรียกข้อมูล(req, res) => {} = Arrow Function ที่ทำงานเมื่อมี Request เข้ามา
req = Request Object (ข้อมูลที่ส่งเข้ามา)res = Response Object (ข้อมูลที่จะส่งกลับไป)res.json(items) = ส่งข้อมูล items ทั้งหมดกลับไปในรูปแบบ JSONตัวอย่างการใช้งาน:
GET http://localhost:3000/api/items
Response (ผลลัพธ์):
[
{ "id": 1, "name": "Item 1", "description": "First item" },
{ "id": 2, "name": "Item 2", "description": "Second item" }
]
app.get('/api/items/:id', (req, res) => {
const item = items.find(i => i.id === parseInt(req.params.id));
if (!item) return res.status(404).json({ message: 'Item not found' });
res.json(item);
});
คำอธิบายแต่ละบรรทัด:
'/api/items/:id' = :id คือ URL Parameter (ตัวแปรใน URL)req.params.id = ดึงค่า id จาก URL ParameterparseInt() = แปลงค่า String เป็นตัวเลข (Number)items.find() = ค้นหา item ที่มี id ตรงกับค่าที่ระบุif (!item) = ตรวจสอบว่าไม่พบ item
res.status(404) = ส่ง HTTP Status Code 404 (Not Found)return = หยุดการทำงาน ไม่ดำเนินการคำสั่งต่อไปres.json(item) = ส่ง item ที่พบกลับไปตัวอย่างการใช้งาน:
GET http://localhost:3000/api/items/1
Response (ผลลัพธ์):
{ "id": 1, "name": "Item 1", "description": "First item" }
app.post('/api/items', (req, res) => {
const newItem = {
id: items.length + 1,
name: req.body.name,
description: req.body.description
};
items.push(newItem);
res.status(201).json(newItem);
});
คำอธิบายแต่ละส่วน:
app.post() = กำหนด Route สำหรับ HTTP POST Request (ใช้สร้างข้อมูลใหม่)req.body = ข้อมูลที่ส่งมาจาก Client ใน Body ของ Requestid: items.length + 1 = สร้าง id ใหม่โดยนับจำนวน items แล้วบวก 1items.push(newItem) = เพิ่ม item ใหม่เข้าไปใน Arrayres.status(201) = ส่ง HTTP Status Code 201 (Created หมายถึง สร้างสำเร็จ)ตัวอย่างการใช้งาน:
POST http://localhost:3000/api/items
Content-Type: application/json
Request Body:
{
"name": "Item 3",
"description": "Third item"
}
Response (ผลลัพธ์):
{
"id": 3,
"name": "Item 3",
"description": "Third item"
}
app.put('/api/items/:id', (req, res) => {
const item = items.find(i => i.id === parseInt(req.params.id));
if (!item) return res.status(404).json({ message: 'Item not found' });
item.name = req.body.name;
item.description = req.body.description;
res.json(item);
});
คำอธิบายขั้นตอนการทำงาน:
app.put() = กำหนด Route สำหรับ HTTP PUT Request (ใช้แก้ไขข้อมูล)name และ descriptionตัวอย่างการใช้งาน:
PUT http://localhost:3000/api/items/1
Content-Type: application/json
Request Body:
{
"name": "Updated Item 1",
"description": "Updated description"
}
Response (ผลลัพธ์):
{
"id": 1,
"name": "Updated Item 1",
"description": "Updated description"
}
app.delete('/api/items/:id', (req, res) => {
const index = items.findIndex(i => i.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ message: 'Item not found' });
items.splice(index, 1);
res.json({ message: 'Item deleted' });
});
คำอธิบายแต่ละส่วน:
app.delete() = กำหนด Route สำหรับ HTTP DELETE Request (ใช้ลบข้อมูล)findIndex() = ค้นหา index (ตำแหน่ง) ของ item ใน Arrayif (index === -1) = ตรวจสอบว่าไม่พบ item (findIndex คืนค่า -1 เมื่อไม่พบ)items.splice(index, 1) = ลบ item ออกจาก Array
index) = ตำแหน่งที่จะเริ่มลบ1) = จำนวน element ที่จะลบตัวอย่างการใช้งาน:
DELETE http://localhost:3000/api/items/1
Response (ผลลัพธ์):
{ "message": "Item deleted" }
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
คำอธิบาย:
app.listen() = เริ่มทำงานของเซิร์ฟเวอร์PORT = หมายเลขพอร์ตที่จะรับฟัง Request (3000)เปิด Command Prompt หรือ Terminal ที่ directory ของโปรเจค แล้วพิมพ์:
node server-api.js
หากเซิร์ฟเวอร์ทำงานสำเร็จ จะเห็นข้อความ:
Server running on http://localhost:3000
มีหลายวิธีในการทดสอบ API:
เปิด Browser แล้วพิมพ์ URL:
http://localhost:3000/api/items
# GET ข้อมูลทั้งหมด
curl http://localhost:3000/api/items
# GET ข้อมูลตาม ID
curl http://localhost:3000/api/items/1
# POST สร้างข้อมูลใหม่
curl -X POST http://localhost:3000/api/items -H "Content-Type: application/json" -d "{\"name\":\"Item 3\",\"description\":\"Third item\"}"
# PUT แก้ไขข้อมูล
curl -X PUT http://localhost:3000/api/items/1 -H "Content-Type: application/json" -d "{\"name\":\"Updated\",\"description\":\"Updated desc\"}"
# DELETE ลบข้อมูล
curl -X DELETE http://localhost:3000/api/items/1
หลังจากสร้าง REST API Server แล้ว ขั้นตอนต่อไปคือการเรียกใช้ API จากหน้าเว็บ (Frontend) มีวิธีหลักๆ 2 วิธี คือ การใช้ Fetch API (Built-in ใน JavaScript) และการใช้ Axios Library
Fetch API เป็น API ที่มีมาพร้อมกับ JavaScript สมัยใหม่ ไม่ต้องติดตั้งเพิ่มเติม ใช้สำหรับส่ง HTTP Request และรับ Response
// ดึงข้อมูล items ทั้งหมด
fetch('http://localhost:3000/api/items')
.then(response => response.json()) // แปลง Response เป็น JSON
.then(data => {
console.log(data); // แสดงข้อมูลใน Console
// นำข้อมูลไปแสดงบนหน้าเว็บ
displayItems(data);
})
.catch(error => {
console.error('เกิดข้อผิดพลาด:', error);
});
คำอธิบาย:
fetch(url) = ส่ง HTTP Request ไปยัง URL ที่กำหนด (Default เป็น GET).then() = รอผลลัพธ์และดำเนินการเมื่อได้รับ Responseresponse.json() = แปลง Response เป็นรูปแบบ JSON.catch() = จัดการข้อผิดพลาดที่อาจเกิดขึ้น// ดึงข้อมูล item ที่มี id = 1
const itemId = 1;
fetch(`http://localhost:3000/api/items/${itemId}`)
.then(response => {
if (!response.ok) {
throw new Error('ไม่พบข้อมูล');
}
return response.json();
})
.then(data => {
console.log('ข้อมูล item:', data);
})
.catch(error => {
console.error('Error:', error);
});
// สร้าง item ใหม่
const newItem = {
name: 'Item 3',
description: 'Third item'
};
fetch('http://localhost:3000/api/items', {
method: 'POST', // ระบุ HTTP Method
headers: {
'Content-Type': 'application/json' // บอกว่าส่งข้อมูลเป็น JSON
},
body: JSON.stringify(newItem) // แปลง Object เป็น JSON String
})
.then(response => response.json())
.then(data => {
console.log('สร้างสำเร็จ:', data);
alert('เพิ่มข้อมูลสำเร็จ!');
})
.catch(error => {
console.error('Error:', error);
});
คำอธิบายพารามิเตอร์ของ fetch():
method = HTTP Method ที่ต้องการใช้ (POST, PUT, DELETE)headers = ข้อมูล Header เช่น Content-Typebody = ข้อมูลที่ต้องการส่งไป (ต้องแปลงเป็น JSON String ด้วย JSON.stringify())// แก้ไข item ที่มี id = 1
const updatedItem = {
name: 'Updated Item 1',
description: 'Updated description'
};
fetch('http://localhost:3000/api/items/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updatedItem)
})
.then(response => response.json())
.then(data => {
console.log('แก้ไขสำเร็จ:', data);
})
.catch(error => {
console.error('Error:', error);
});
// ลบ item ที่มี id = 1
fetch('http://localhost:3000/api/items/1', {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
console.log(data.message); // "Item deleted"
alert('ลบข้อมูลสำเร็จ!');
})
.catch(error => {
console.error('Error:', error);
});
// GET ข้อมูลด้วย async/await
async function getItems() {
try {
const response = await fetch('http://localhost:3000/api/items');
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('Error:', error);
}
}
// POST ข้อมูลด้วย async/await
async function createItem(itemData) {
try {
const response = await fetch('http://localhost:3000/api/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(itemData)
});
const data = await response.json();
console.log('สร้างสำเร็จ:', data);
return data;
} catch (error) {
console.error('Error:', error);
}
}
// เรียกใช้งาน
getItems();
createItem({ name: 'Item 4', description: 'Fourth item' });
ข้อดีของ Async/Await:
Axios เป็น HTTP Client Library ที่นิยมใช้กัน มีฟีเจอร์มากกว่า Fetch API และใช้งานง่ายกว่า
วิธีที่ 1: ใช้ CDN (เหมาะสำหรับไฟล์ HTML เดี่ยว)
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
วิธีที่ 2: ติดตั้งผ่าน npm (สำหรับโปรเจคที่ใช้ Node.js)
npm install axios
// ดึงข้อมูล items ทั้งหมด
axios.get('http://localhost:3000/api/items')
.then(response => {
console.log(response.data); // ข้อมูลอยู่ใน response.data
displayItems(response.data);
})
.catch(error => {
console.error('Error:', error);
});
ความแตกต่างจาก Fetch:
.json() เพราะ Axios แปลงให้อัตโนมัติresponse.data แทนที่จะเป็น response โดยตรง// ดึงข้อมูล item ที่มี id = 1
axios.get('http://localhost:3000/api/items/1')
.then(response => {
console.log('ข้อมูล item:', response.data);
})
.catch(error => {
if (error.response) {
// Server ตอบกลับมาแต่มี Error (404, 500, etc.)
console.error('Error status:', error.response.status);
console.error('Error data:', error.response.data);
} else if (error.request) {
// ส่ง Request ไปแล้วแต่ไม่ได้รับ Response
console.error('No response:', error.request);
} else {
// เกิด Error อื่นๆ
console.error('Error:', error.message);
}
});
// สร้าง item ใหม่
const newItem = {
name: 'Item 3',
description: 'Third item'
};
axios.post('http://localhost:3000/api/items', newItem)
.then(response => {
console.log('สร้างสำเร็จ:', response.data);
alert('เพิ่มข้อมูลสำเร็จ!');
})
.catch(error => {
console.error('Error:', error);
});
สังเกต: ไม่ต้องตั้งค่า headers และไม่ต้องใช้ JSON.stringify() เพราะ Axios จัดการให้อัตโนมัติ
// แก้ไข item ที่มี id = 1
const updatedItem = {
name: 'Updated Item 1',
description: 'Updated description'
};
axios.put('http://localhost:3000/api/items/1', updatedItem)
.then(response => {
console.log('แก้ไขสำเร็จ:', response.data);
})
.catch(error => {
console.error('Error:', error);
});
// ลบ item ที่มี id = 1
axios.delete('http://localhost:3000/api/items/1')
.then(response => {
console.log(response.data.message);
alert('ลบข้อมูลสำเร็จ!');
})
.catch(error => {
console.error('Error:', error);
});
// GET ข้อมูลด้วย async/await
async function getItems() {
try {
const response = await axios.get('http://localhost:3000/api/items');
console.log(response.data);
return response.data;
} catch (error) {
console.error('Error:', error);
}
}
// POST ข้อมูลด้วย async/await
async function createItem(itemData) {
try {
const response = await axios.post('http://localhost:3000/api/items', itemData);
console.log('สร้างสำเร็จ:', response.data);
return response.data;
} catch (error) {
console.error('Error:', error);
}
}
// PUT แก้ไขข้อมูล
async function updateItem(id, itemData) {
try {
const response = await axios.put(`http://localhost:3000/api/items/${id}`, itemData);
console.log('แก้ไขสำเร็จ:', response.data);
return response.data;
} catch (error) {
console.error('Error:', error);
}
}
// DELETE ลบข้อมูล
async function deleteItem(id) {
try {
const response = await axios.delete(`http://localhost:3000/api/items/${id}`);
console.log(response.data.message);
return response.data;
} catch (error) {
console.error('Error:', error);
}
}
// เรียกใช้งาน
getItems();
createItem({ name: 'Item 4', description: 'Fourth item' });
updateItem(1, { name: 'Updated', description: 'Updated desc' });
deleteItem(2);
ตัวอย่างการสร้างหน้าเว็บที่เรียกใช้ API สำหรับจัดการข้อมูล Items
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Item Manager - Fetch API</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.item {
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
}
button {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #5568d3;
}
input, textarea {
width: 100%;
padding: 8px;
margin: 5px 0;
border: 1px solid #ddd;
border-radius: 3px;
}
</style>
</head>
<body>
<h1>Item Manager (Fetch API)</h1>
<!-- ฟอร์มเพิ่มข้อมูล -->
<div>
<h2>เพิ่ม Item ใหม่</h2>
<input type="text" id="itemName" placeholder="ชื่อ Item">
<textarea id="itemDesc" placeholder="คำอธิบาย"></textarea>
<button onclick="addItem()">เพิ่ม Item</button>
</div>
<!-- แสดงรายการ Items -->
<div>
<h2>รายการ Items</h2>
<button onclick="loadItems()">โหลดข้อมูล</button>
<div id="itemsList"></div>
</div>
<script>
const API_URL = 'http://localhost:3000/api/items';
// โหลดข้อมูลทั้งหมด
async function loadItems() {
try {
const response = await fetch(API_URL);
const items = await response.json();
displayItems(items);
} catch (error) {
console.error('Error:', error);
alert('ไม่สามารถโหลดข้อมูลได้');
}
}
// แสดงข้อมูลบนหน้าเว็บ
function displayItems(items) {
const container = document.getElementById('itemsList');
container.innerHTML = '';
items.forEach(item => {
const div = document.createElement('div');
div.className = 'item';
div.innerHTML = `
<h3>${item.name}</h3>
<p>${item.description}</p>
<button onclick="editItem(${item.id})">แก้ไข</button>
<button onclick="deleteItem(${item.id})">ลบ</button>
`;
container.appendChild(div);
});
}
// เพิ่มข้อมูลใหม่
async function addItem() {
const name = document.getElementById('itemName').value;
const description = document.getElementById('itemDesc').value;
if (!name || !description) {
alert('กรุณากรอกข้อมูลให้ครบถ้วน');
return;
}
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name, description })
});
const data = await response.json();
alert('เพิ่มข้อมูลสำเร็จ!');
document.getElementById('itemName').value = '';
document.getElementById('itemDesc').value = '';
loadItems();
} catch (error) {
console.error('Error:', error);
alert('เกิดข้อผิดพลาด');
}
}
// ลบข้อมูล
async function deleteItem(id) {
if (!confirm('คุณต้องการลบข้อมูลนี้หรือไม่?')) return;
try {
await fetch(`${API_URL}/${id}`, {
method: 'DELETE'
});
alert('ลบข้อมูลสำเร็จ!');
loadItems();
} catch (error) {
console.error('Error:', error);
alert('เกิดข้อผิดพลาด');
}
}
// โหลดข้อมูลเมื่อเปิดหน้าเว็บ
window.onload = loadItems;
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Item Manager - Axios</title>
<!-- เพิ่ม Axios CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.item {
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
background: #f9f9f9;
}
button {
background: #764ba2;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #623a85;
}
button.delete {
background: #e74c3c;
}
button.delete:hover {
background: #c0392b;
}
input, textarea {
width: 100%;
padding: 8px;
margin: 5px 0;
border: 1px solid #ddd;
border-radius: 3px;
}
</style>
</head>
<body>
<h1>Item Manager (Axios)</h1>
<!-- ฟอร์มเพิ่มข้อมูล -->
<div>
<h2>เพิ่ม Item ใหม่</h2>
<input type="text" id="itemName" placeholder="ชื่อ Item">
<textarea id="itemDesc" placeholder="คำอธิบาย"></textarea>
<button onclick="addItem()">เพิ่ม Item</button>
</div>
<!-- แสดงรายการ Items -->
<div>
<h2>รายการ Items</h2>
<button onclick="loadItems()">โหลดข้อมูล</button>
<div id="itemsList"></div>
</div>
<script>
const API_URL = 'http://localhost:3000/api/items';
// โหลดข้อมูลทั้งหมด
async function loadItems() {
try {
const response = await axios.get(API_URL);
displayItems(response.data);
} catch (error) {
console.error('Error:', error);
alert('ไม่สามารถโหลดข้อมูลได้');
}
}
// แสดงข้อมูลบนหน้าเว็บ
function displayItems(items) {
const container = document.getElementById('itemsList');
container.innerHTML = '';
items.forEach(item => {
const div = document.createElement('div');
div.className = 'item';
div.innerHTML = `
<h3>${item.name} <small>(ID: ${item.id})</small></h3>
<p>${item.description}</p>
<button onclick="editItem(${item.id})">แก้ไข</button>
<button class="delete" onclick="deleteItem(${item.id})">ลบ</button>
`;
container.appendChild(div);
});
}
// เพิ่มข้อมูลใหม่
async function addItem() {
const name = document.getElementById('itemName').value;
const description = document.getElementById('itemDesc').value;
if (!name || !description) {
alert('กรุณากรอกข้อมูลให้ครบถ้วน');
return;
}
try {
const response = await axios.post(API_URL, {
name,
description
});
alert('เพิ่มข้อมูลสำเร็จ!');
document.getElementById('itemName').value = '';
document.getElementById('itemDesc').value = '';
loadItems();
} catch (error) {
console.error('Error:', error);
alert('เกิดข้อผิดพลาด');
}
}
// แก้ไขข้อมูล
async function editItem(id) {
const name = prompt('ชื่อใหม่:');
const description = prompt('คำอธิบายใหม่:');
if (!name || !description) return;
try {
await axios.put(`${API_URL}/${id}`, {
name,
description
});
alert('แก้ไขข้อมูลสำเร็จ!');
loadItems();
} catch (error) {
console.error('Error:', error);
alert('เกิดข้อผิดพลาด');
}
}
// ลบข้อมูล
async function deleteItem(id) {
if (!confirm('คุณต้องการลบข้อมูลนี้หรือไม่?')) return;
try {
await axios.delete(`${API_URL}/${id}`);
alert('ลบข้อมูลสำเร็จ!');
loadItems();
} catch (error) {
console.error('Error:', error);
alert('เกิดข้อผิดพลาด');
}
}
// โหลดข้อมูลเมื่อเปิดหน้าเว็บ
window.onload = loadItems;
</script>
</body>
</html>
| รายการ | Fetch API | Axios |
|---|---|---|
| ติดตั้ง | Built-in (ไม่ต้องติดตั้ง) | ต้องติดตั้งเพิ่ม |
| แปลง JSON | ต้องเรียก .json() เอง | แปลงอัตโนมัติ |
| ส่งข้อมูล | ต้อง JSON.stringify() | ส่ง Object ได้เลย |
| Error Handling | ซับซ้อนกว่า | ง่ายและชัดเจนกว่า |
| Browser Support | Modern browsers | รองรับเก่าได้ดีกว่า |
| ขนาดไฟล์ | 0 KB (Built-in) | ~13 KB (gzipped) |
คำแนะนำในการเลือกใช้:
ปัญหา CORS (Cross-Origin Resource Sharing):
หากเรียก API จากโดเมนอื่น (เช่น หน้าเว็บรันที่ file:// หรือ localhost:8080 แต่ API อยู่ที่ localhost:3000) จะเจอ Error:
Access to fetch at 'http://localhost:3000/api/items' from origin 'null'
has been blocked by CORS policy
วิธีแก้: เพิ่ม CORS Middleware ใน server-api.js
// ติดตั้ง CORS
npm install cors
// เพิ่มในไฟล์ server-api.js
const express = require('express');
const cors = require('cors'); // เพิ่มบรรทัดนี้
const app = express();
// เพิ่ม CORS Middleware
app.use(cors()); // อนุญาตทุกโดเมน
// หรือ
app.use(cors({
origin: 'http://localhost:8080' // อนุญาตเฉพาะโดเมนนี้
}));
app.use(express.json());
// ... โค้ดส่วนอื่นๆ
รหัสสถานะ HTTP (HTTP Status Codes) เป็นตัวเลข 3 หลักที่เซิร์ฟเวอร์ส่งกลับมาเพื่อบอกผลลัพธ์ของการประมวลผล Request
| Status Code | ความหมาย | ใช้เมื่อ |
|---|---|---|
| 200 | OK | Request สำเร็จ (ใช้กับ GET, PUT) |
| 201 | Created | สร้างข้อมูลสำเร็จ (ใช้กับ POST) |
| 404 | Not Found | ไม่พบข้อมูลที่ต้องการ |
HTTP Status Codes ที่ควรรู้จักเพิ่มเติม:
CRUD เป็นตัวย่อของคำศัพท์ในการจัดการข้อมูลพื้นฐาน 4 ประเภท:
| Operation | HTTP Method | Endpoint | คำอธิบาย |
|---|---|---|---|
| Create | POST | /api/items | สร้าง item ใหม่ |
| Read All | GET | /api/items | ดึงข้อมูลทั้งหมด |
| Read One | GET | /api/items/:id | ดึงข้อมูล 1 รายการ |
| Update | PUT | /api/items/:id | แก้ไขข้อมูล |
| Delete | DELETE | /api/items/:id | ลบข้อมูล |
ข้อควรจำ: CRUD Operations เป็นพื้นฐานสำคัญที่พบได้ในทุกระบบจัดการข้อมูล ไม่ว่าจะเป็น Web Application, Mobile Application หรือ Desktop Application
ในส่วนนี้จะสอนวิธีการเชื่อมต่อ REST API กับฐานข้อมูล MySQL โดยใช้ XAMPP เป็นเซิร์ฟเวอร์ฐานข้อมูล แทนการเก็บข้อมูลในหน่วยความจำ (In-Memory) ซึ่งจะทำให้ข้อมูลไม่หายเมื่อปิดเซิร์ฟเวอร์
XAMPP คือ ชุดซอฟต์แวร์ที่รวม Apache Web Server, MySQL Database, PHP และ Perl ไว้ในแพ็กเกจเดียว
rest_api_dbutf8mb4_unicode_ciรัน SQL Command ต่อไปนี้ใน phpMyAdmin:
CREATE TABLE items (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- เพิ่มข้อมูลตัวอย่าง
INSERT INTO items (name, description) VALUES
('Item 1', 'First item'),
('Item 2', 'Second item');
คำอธิบายโครงสร้างตาราง:
id = Primary Key, เพิ่มค่าอัตโนมัติ (AUTO_INCREMENT)name = ชื่อ item (ห้ามว่าง)description = คำอธิบาย (อนุญาตให้ว่างได้)created_at = วันที่สร้าง (บันทึกอัตโนมัติ)updated_at = วันที่แก้ไขล่าสุด (อัปเดตอัตโนมัติ)เปิด Terminal ที่โฟลเดอร์โปรเจค แล้วรันคำสั่ง:
npm install mysql2
mysql2 คือ MySQL client สำหรับ Node.js ที่รองรับ Promise และมีประสิทธิภาพสูง
สร้างไฟล์ใหม่ชื่อ server-mysql.js สำหรับเชื่อมต่อกับ MySQL:
const express = require('express');
const mysql = require('mysql2');
const app = express();
const PORT = 3000;
app.use(express.json());
// สร้างการเชื่อมต่อกับ MySQL
const connection = mysql.createConnection({
host: 'localhost', // ที่อยู่เซิร์ฟเวอร์
user: 'root', // ชื่อผู้ใช้ (Default ของ XAMPP คือ root)
password: '', // รหัสผ่าน (Default ของ XAMPP คือว่างเปล่า)
database: 'rest_api_db' // ชื่อฐานข้อมูล
});
// ทดสอบการเชื่อมต่อ
connection.connect((err) => {
if (err) {
console.error('เชื่อมต่อฐานข้อมูลล้มเหลว:', err);
return;
}
console.log('เชื่อมต่อกับ MySQL สำเร็จ!');
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
const express = require('express');
const mysql = require('mysql2');
const app = express();
const PORT = 3000;
app.use(express.json());
// สร้าง Connection Pool สำหรับจัดการการเชื่อมต่อหลายๆ ตัว
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'rest_api_db',
waitForConnections: true, // รอถ้าการเชื่อมต่อเต็ม
connectionLimit: 10, // จำนวนการเชื่อมต่อสูงสุด
queueLimit: 0 // ไม่จำกัดคิว
});
// ใช้ Promise API (สำหรับ async/await)
const promisePool = pool.promise();
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
ข้อดีของ Connection Pool:
ตัวอย่างโค้ดเต็มรูปแบบของ REST API ที่เชื่อมต่อกับ MySQL:
// GET /api/items - ดึงข้อมูลทั้งหมด
app.get('/api/items', async (req, res) => {
try {
const [rows] = await promisePool.query('SELECT * FROM items');
res.json(rows);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการดึงข้อมูล' });
}
});
คำอธิบาย:
promisePool.query() = รัน SQL Query และคืนค่าเป็น Promise[rows] = Destructuring เพื่อดึงผลลัพธ์ (แถวแรกคือข้อมูล แถวที่สองคือ metadata)try...catch = จัดการ Error ที่อาจเกิดขึ้นstatus(500) = ส่ง HTTP Status Code 500 เมื่อเกิด Server Error// GET /api/items/:id - ดึงข้อมูลตาม ID
app.get('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[id]
);
if (rows.length === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
res.json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการดึงข้อมูล' });
}
});
ความปลอดภัย - SQL Injection Prevention:
ใช้ ? (placeholder) ในคำสั่ง SQL และส่งค่าเป็น Array แยกต่างหาก แทนการใส่ค่าตรงๆ ในคำสั่ง SQL
// ❌ ไม่ปลอดภัย (เสี่ยง SQL Injection)
const query = `SELECT * FROM items WHERE id = ${id}`;
// ✅ ปลอดภัย (ใช้ Prepared Statement)
const query = 'SELECT * FROM items WHERE id = ?';
const [rows] = await promisePool.query(query, [id]);
// POST /api/items - สร้างข้อมูลใหม่
app.post('/api/items', async (req, res) => {
try {
const { name, description } = req.body;
// Validation
if (!name || !description) {
return res.status(400).json({
message: 'กรุณากรอกข้อมูลให้ครบถ้วน'
});
}
// Insert ข้อมูลเข้าฐานข้อมูล
const [result] = await promisePool.query(
'INSERT INTO items (name, description) VALUES (?, ?)',
[name, description]
);
// ดึงข้อมูลที่เพิ่งสร้างกลับมา
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[result.insertId]
);
res.status(201).json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการสร้างข้อมูล' });
}
});
คำอธิบาย:
result.insertId = ID ของแถวที่เพิ่งถูก Insertresult.affectedRows = จำนวนแถวที่ถูกกระทบ// PUT /api/items/:id - แก้ไขข้อมูล
app.put('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const { name, description } = req.body;
// Validation
if (!name || !description) {
return res.status(400).json({
message: 'กรุณากรอกข้อมูลให้ครบถ้วน'
});
}
// อัปเดตข้อมูล
const [result] = await promisePool.query(
'UPDATE items SET name = ?, description = ? WHERE id = ?',
[name, description, id]
);
// ตรวจสอบว่ามีการอัปเดตหรือไม่
if (result.affectedRows === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
// ดึงข้อมูลที่อัปเดตแล้วกลับมา
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[id]
);
res.json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการแก้ไขข้อมูล' });
}
});
// DELETE /api/items/:id - ลบข้อมูล
app.delete('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const [result] = await promisePool.query(
'DELETE FROM items WHERE id = ?',
[id]
);
if (result.affectedRows === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
res.json({ message: 'ลบข้อมูลสำเร็จ' });
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการลบข้อมูล' });
}
});
โค้ดสำเร็จรูปทั้งหมด:
const express = require('express');
const mysql = require('mysql2');
const cors = require('cors');
const app = express();
const PORT = 3000;
// Middleware
app.use(cors());
app.use(express.json());
// สร้าง Connection Pool
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'rest_api_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
const promisePool = pool.promise();
// =============== API Endpoints ===============
// GET /api/items - ดึงข้อมูลทั้งหมด
app.get('/api/items', async (req, res) => {
try {
const [rows] = await promisePool.query('SELECT * FROM items');
res.json(rows);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการดึงข้อมูล' });
}
});
// GET /api/items/:id - ดึงข้อมูลตาม ID
app.get('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[id]
);
if (rows.length === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
res.json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการดึงข้อมูล' });
}
});
// POST /api/items - สร้างข้อมูลใหม่
app.post('/api/items', async (req, res) => {
try {
const { name, description } = req.body;
if (!name || !description) {
return res.status(400).json({
message: 'กรุณากรอกข้อมูลให้ครบถ้วน'
});
}
const [result] = await promisePool.query(
'INSERT INTO items (name, description) VALUES (?, ?)',
[name, description]
);
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[result.insertId]
);
res.status(201).json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการสร้างข้อมูล' });
}
});
// PUT /api/items/:id - แก้ไขข้อมูล
app.put('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const { name, description } = req.body;
if (!name || !description) {
return res.status(400).json({
message: 'กรุณากรอกข้อมูลให้ครบถ้วน'
});
}
const [result] = await promisePool.query(
'UPDATE items SET name = ?, description = ? WHERE id = ?',
[name, description, id]
);
if (result.affectedRows === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
const [rows] = await promisePool.query(
'SELECT * FROM items WHERE id = ?',
[id]
);
res.json(rows[0]);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการแก้ไขข้อมูล' });
}
});
// DELETE /api/items/:id - ลบข้อมูล
app.delete('/api/items/:id', async (req, res) => {
try {
const { id } = req.params;
const [result] = await promisePool.query(
'DELETE FROM items WHERE id = ?',
[id]
);
if (result.affectedRows === 0) {
return res.status(404).json({ message: 'ไม่พบข้อมูล' });
}
res.json({ message: 'ลบข้อมูลสำเร็จ' });
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ message: 'เกิดข้อผิดพลาดในการลบข้อมูล' });
}
});
// เริ่มเซิร์ฟเวอร์
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
ขั้นตอนการรัน:
node server-mysql.jsแนวทางปฏิบัติที่ดี:
สร้างไฟล์ db.config.js สำหรับการตั้งค่า:
// db.config.js
module.exports = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'rest_api_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
};
นำไปใช้ใน server-mysql.js:
const express = require('express');
const mysql = require('mysql2');
const dbConfig = require('./db.config');
const pool = mysql.createPool(dbConfig);
const promisePool = pool.promise();
GET /api/items/search?name=keywordโจทย์: เพิ่มฟิลด์ price (ราคา) และ quantity (จำนวน) ให้กับ item
คำแนะนำ:
โจทย์: เพิ่มการตรวจสอบข้อมูลใน POST endpoint
เงื่อนไข:
โจทย์: สร้าง API สำหรับจัดการข้อมูล Users
โครงสร้างข้อมูล:
Endpoints ที่ต้องสร้าง:
โจทย์: เพิ่ม endpoint สำหรับค้นหา items ตามชื่อ
ตัวอย่าง:
GET /api/items/search?name=Item 1
คำแนะนำ: ใช้ req.query.name และ filter() method