คู่มือเรียนรู้ REST API Server ด้วย Express.js

เอกสารประกอบการศึกษาสำหรับไฟล์ server-api.js


1. บทนำ

ไฟล์ 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 ในการดำเนินการต่างๆ กับข้อมูล


2. วัตถุประสงค์การเรียนรู้

เมื่อศึกษาเอกสารฉบับนี้แล้ว นักเรียนจะสามารถ:

  1. เข้าใจหลักการสร้าง API Server พื้นฐาน
  2. เข้าใจการทำงานของ HTTP Methods (GET, POST, PUT, DELETE)
  3. เข้าใจและสามารถนำไปประยุกต์ใช้การจัดการข้อมูลแบบ CRUD (Create, Read, Update, Delete)
  4. เข้าใจและสามารถใช้งาน Express.js ในการสร้าง Web Server

3. การติดตั้งและเตรียมความพร้อม

3.1 สิ่งที่จำเป็นต้องมี

3.2 ขั้นตอนการติดตั้ง

เปิด Command Prompt หรือ Terminal แล้วพิมพ์คำสั่งดังนี้:

# สร้างโปรเจคใหม่
npm init -y

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

คำอธิบาย:

  • npm init -y = สร้างไฟล์ package.json สำหรับจัดการ dependencies ของโปรเจค
  • npm install express = ติดตั้ง Express.js framework เข้าสู่โปรเจค

4. คำอธิบายโค้ดทีละส่วน

4.1 การนำเข้า (Import) และการตั้งค่าเริ่มต้น

const express = require('express');

const app = express();
const PORT = 3000;

คำอธิบายแต่ละบรรทัด:

  • require('express') = นำเข้าโมดูล Express.js เข้ามาใช้งานในไฟล์
  • app = express() = สร้าง instance ของแอพพลิเคชัน Express ใหม่
  • PORT = 3000 = กำหนดหมายเลขพอร์ตที่เซิร์ฟเวอร์จะทำงาน (สามารถเปลี่ยนเป็นเลขอื่นได้)

4.2 Middleware (ตัวกลางประมวลผล)

app.use(express.json());

คำอธิบาย:

  • Middleware หมายถึง ฟังก์ชันที่ทำงานระหว่างการได้รับ Request และการส่ง Response กลับ
  • express.json() = แปลงข้อมูล JSON ที่ส่งมาจาก Client ให้เป็น JavaScript Object ที่สามารถนำมาใช้งานได้
  • เหตุผลในการใช้: ข้อมูลที่ส่งมาจาก Client จะอยู่ในรูปแบบ JSON String จำเป็นต้องแปลงให้เป็น Object ก่อนนำไปประมวลผล

4.3 In-Memory Data Store (ที่เก็บข้อมูลในหน่วยความจำ)

let items = [
    { id: 1, name: 'Item 1', description: 'First item' },
    { id: 2, name: 'Item 2', description: 'Second item' }
];

คำอธิบาย:

  • ใช้ Array ในการเก็บข้อมูลชั่วคราวในหน่วยความจำ (RAM)
  • แต่ละ item ประกอบด้วย 3 property:
    • id = รหัสประจำตัวของ item (ไม่ซ้ำกัน)
    • name = ชื่อของ item
    • description = คำอธิบาย item

หมายเหตุสำคัญ:

  • ข้อมูลในหน่วยความจำจะหายไปเมื่อปิดเซิร์ฟเวอร์
  • ในการพัฒนาระบบจริง ควรใช้ฐานข้อมูลถาวร เช่น MongoDB, MySQL หรือ PostgreSQL

5. API Endpoints (จุดเชื่อมต่อ API)

5.1 GET - ดึงข้อมูลทั้งหมด

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" }
]

5.2 GET - ดึงข้อมูลตาม ID

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 Parameter
  • parseInt() = แปลงค่า 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" }

5.3 POST - สร้างข้อมูลใหม่

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 ของ Request
  • id: items.length + 1 = สร้าง id ใหม่โดยนับจำนวน items แล้วบวก 1
  • items.push(newItem) = เพิ่ม item ใหม่เข้าไปใน Array
  • res.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"
}

5.4 PUT - แก้ไขข้อมูล

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);
});

คำอธิบายขั้นตอนการทำงาน:

  1. app.put() = กำหนด Route สำหรับ HTTP PUT Request (ใช้แก้ไขข้อมูล)
  2. ค้นหา item ที่ต้องการแก้ไขโดยใช้ id
  3. ถ้าไม่พบ item ส่ง Error 404 กลับไป
  4. ถ้าพบ item ทำการอัพเดตค่า name และ description
  5. ส่ง item ที่แก้ไขแล้วกลับไป

ตัวอย่างการใช้งาน:

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"
}

5.5 DELETE - ลบข้อมูล

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 ใน Array
  • if (index === -1) = ตรวจสอบว่าไม่พบ item (findIndex คืนค่า -1 เมื่อไม่พบ)
  • items.splice(index, 1) = ลบ item ออกจาก Array
    • Parameter ตัวแรก (index) = ตำแหน่งที่จะเริ่มลบ
    • Parameter ตัวที่สอง (1) = จำนวน element ที่จะลบ
  • ส่งข้อความยืนยันการลบกลับไป

ตัวอย่างการใช้งาน:

DELETE http://localhost:3000/api/items/1

Response (ผลลัพธ์):
{ "message": "Item deleted" }

5.6 เริ่มทำงานของเซิร์ฟเวอร์

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

คำอธิบาย:

  • app.listen() = เริ่มทำงานของเซิร์ฟเวอร์
  • PORT = หมายเลขพอร์ตที่จะรับฟัง Request (3000)
  • Arrow Function ที่สอง = ทำงานเมื่อเซิร์ฟเวอร์เริ่มทำงานสำเร็จ
  • แสดงข้อความใน Console เพื่อยืนยันว่าเซิร์ฟเวอร์พร้อมใช้งาน

6. วิธีการรันโปรแกรม

6.1 ขั้นตอนที่ 1: เริ่มเซิร์ฟเวอร์

เปิด Command Prompt หรือ Terminal ที่ directory ของโปรเจค แล้วพิมพ์:

node server-api.js

หากเซิร์ฟเวอร์ทำงานสำเร็จ จะเห็นข้อความ:

Server running on http://localhost:3000

6.2 ขั้นตอนที่ 2: ทดสอบ API

มีหลายวิธีในการทดสอบ API:

วิธีที่ 1: ใช้ Web Browser (สำหรับ GET Request เท่านั้น)

เปิด Browser แล้วพิมพ์ URL:

http://localhost:3000/api/items

วิธีที่ 2: ใช้โปรแกรม Postman

  1. ดาวน์โหลดและติดตั้งโปรแกรม Postman
  2. สร้าง Request ใหม่
  3. เลือก HTTP Method (GET, POST, PUT, DELETE)
  4. ใส่ URL ของ API
  5. สำหรับ POST และ PUT ให้ใส่ข้อมูลใน Body ในรูปแบบ JSON
  6. กดปุ่ม Send เพื่อส่ง Request

วิธีที่ 3: ใช้คำสั่ง curl (Command Line)

# 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

7. การใช้งาน API บนหน้าเว็บ (HTML)

หลังจากสร้าง REST API Server แล้ว ขั้นตอนต่อไปคือการเรียกใช้ API จากหน้าเว็บ (Frontend) มีวิธีหลักๆ 2 วิธี คือ การใช้ Fetch API (Built-in ใน JavaScript) และการใช้ Axios Library

7.1 การใช้งาน Fetch API

Fetch API เป็น API ที่มีมาพร้อมกับ JavaScript สมัยใหม่ ไม่ต้องติดตั้งเพิ่มเติม ใช้สำหรับส่ง HTTP Request และรับ Response

7.1.1 GET - ดึงข้อมูลทั้งหมด

// ดึงข้อมูล 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() = รอผลลัพธ์และดำเนินการเมื่อได้รับ Response
  • response.json() = แปลง Response เป็นรูปแบบ JSON
  • .catch() = จัดการข้อผิดพลาดที่อาจเกิดขึ้น

7.1.2 GET - ดึงข้อมูลตาม ID

// ดึงข้อมูล 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);
    });

7.1.3 POST - สร้างข้อมูลใหม่

// สร้าง 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-Type
  • body = ข้อมูลที่ต้องการส่งไป (ต้องแปลงเป็น JSON String ด้วย JSON.stringify())

7.1.4 PUT - แก้ไขข้อมูล

// แก้ไข 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);
    });

7.1.5 DELETE - ลบข้อมูล

// ลบ 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);
    });

7.1.6 การใช้ Async/Await (วิธีที่สะดวกกว่า)

// 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:

  • โค้ดอ่านง่ายกว่า .then() chain
  • จัดการ Error ด้วย try/catch ได้สะดวก
  • เขียนโค้ดได้เหมือนโค้ดแบบ Synchronous

7.2 การใช้งาน Axios

Axios เป็น HTTP Client Library ที่นิยมใช้กัน มีฟีเจอร์มากกว่า Fetch API และใช้งานง่ายกว่า

7.2.1 การติดตั้ง Axios

วิธีที่ 1: ใช้ CDN (เหมาะสำหรับไฟล์ HTML เดี่ยว)

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

วิธีที่ 2: ติดตั้งผ่าน npm (สำหรับโปรเจคที่ใช้ Node.js)

npm install axios

7.2.2 GET - ดึงข้อมูลทั้งหมด

// ดึงข้อมูล 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 โดยตรง
  • จัดการ HTTP Error ได้ง่ายกว่า

7.2.3 GET - ดึงข้อมูลตาม ID

// ดึงข้อมูล 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);
        }
    });

7.2.4 POST - สร้างข้อมูลใหม่

// สร้าง 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 จัดการให้อัตโนมัติ

7.2.5 PUT - แก้ไขข้อมูล

// แก้ไข 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);
    });

7.2.6 DELETE - ลบข้อมูล

// ลบ 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);
    });

7.2.7 การใช้ Async/Await กับ Axios

// 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);

7.3 ตัวอย่างหน้าเว็บ HTML สมบูรณ์

ตัวอย่างการสร้างหน้าเว็บที่เรียกใช้ API สำหรับจัดการข้อมูล Items

ตัวอย่างที่ 1: ใช้ Fetch API

<!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>

ตัวอย่างที่ 2: ใช้ Axios

<!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>

7.4 เปรียบเทียบ Fetch API vs Axios

รายการ Fetch API Axios
ติดตั้ง Built-in (ไม่ต้องติดตั้ง) ต้องติดตั้งเพิ่ม
แปลง JSON ต้องเรียก .json() เอง แปลงอัตโนมัติ
ส่งข้อมูล ต้อง JSON.stringify() ส่ง Object ได้เลย
Error Handling ซับซ้อนกว่า ง่ายและชัดเจนกว่า
Browser Support Modern browsers รองรับเก่าได้ดีกว่า
ขนาดไฟล์ 0 KB (Built-in) ~13 KB (gzipped)

คำแนะนำในการเลือกใช้:

  • ใช้ Fetch API เมื่อ: โปรเจคเล็ก ไม่ต้องการ dependency เพิ่ม ต้องการขนาดไฟล์เล็ก
  • ใช้ Axios เมื่อ: โปรเจคใหญ่ ต้องการฟีเจอร์เพิ่มเติม ต้องการโค้ดที่อ่านง่ายและจัดการ Error ได้ดีกว่า

7.5 การแก้ปัญหา CORS

ปัญหา 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());
// ... โค้ดส่วนอื่นๆ

8. HTTP Status Codes

รหัสสถานะ HTTP (HTTP Status Codes) เป็นตัวเลข 3 หลักที่เซิร์ฟเวอร์ส่งกลับมาเพื่อบอกผลลัพธ์ของการประมวลผล Request

Status Code ความหมาย ใช้เมื่อ
200 OK Request สำเร็จ (ใช้กับ GET, PUT)
201 Created สร้างข้อมูลสำเร็จ (ใช้กับ POST)
404 Not Found ไม่พบข้อมูลที่ต้องการ

HTTP Status Codes ที่ควรรู้จักเพิ่มเติม:

  • 400 Bad Request = Request ไม่ถูกต้อง
  • 401 Unauthorized = ไม่ได้รับอนุญาต ต้อง Login
  • 403 Forbidden = ห้ามเข้าถึง
  • 500 Internal Server Error = เกิดข้อผิดพลาดที่เซิร์ฟเวอร์

9. CRUD Operations

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


10. แนวทางการพัฒนาต่อ

สำหรับระดับพื้นฐาน

  1. เพิ่ม Validation - ตรวจสอบความถูกต้องของข้อมูลที่ส่งเข้ามา
  2. เพิ่ม Error Handling - จัดการข้อผิดพลาดที่อาจเกิดขึ้น
  3. เพิ่ม CORS - อนุญาตให้ Frontend จากโดเมนอื่นเรียกใช้ได้

สำหรับระดับกลาง

  1. เชื่อมต่อฐานข้อมูลจริง - ใช้ MongoDB, MySQL หรือ PostgreSQL
  2. เพิ่มระบบ Authentication - ระบบ Login/Register และ JWT Token
  3. เพิ่ม Pagination - แบ่งหน้าสำหรับข้อมูลจำนวนมาก

สำหรับระดับสูง

  1. ใช้ TypeScript - เพิ่มประสิทธิภาพและความปลอดภัยของโค้ด
  2. เพิ่ม Testing - ใช้ Jest หรือ Mocha ทดสอบโค้ด
  3. Deploy - นำขึ้น Cloud เช่น Heroku, AWS หรือ Azure

11. เคล็ดลับการเรียนรู้

  1. ทำความเข้าใจ HTTP Methods - ศึกษาให้เข้าใจว่าแต่ละ Method มีวัตถุประสงค์อย่างไร
  2. ทดลองแก้ไขโค้ด - ลองเปลี่ยนค่าต่างๆ เพื่อดูผลลัพธ์ที่เกิดขึ้น
  3. ใช้ console.log() - แสดงค่าตัวแปรเพื่อตรวจสอบระหว่างการทำงาน
  4. อ่าน Error Messages - ข้อความ Error มักจะบอกสาเหตุของปัญหาอยู่แล้ว
  5. ฝึกฝนเป็นประจำ - ลองสร้าง API สำหรับข้อมูลประเภทอื่นๆ เช่น Users, Products, Books

11. การใช้งาน REST API ร่วมกับ MySQL (XAMPP)

ในส่วนนี้จะสอนวิธีการเชื่อมต่อ REST API กับฐานข้อมูล MySQL โดยใช้ XAMPP เป็นเซิร์ฟเวอร์ฐานข้อมูล แทนการเก็บข้อมูลในหน่วยความจำ (In-Memory) ซึ่งจะทำให้ข้อมูลไม่หายเมื่อปิดเซิร์ฟเวอร์

11.1 การติดตั้งและเตรียมความพร้อม

ขั้นตอนที่ 1: ติดตั้ง XAMPP

XAMPP คือ ชุดซอฟต์แวร์ที่รวม Apache Web Server, MySQL Database, PHP และ Perl ไว้ในแพ็กเกจเดียว

  1. ดาวน์โหลด XAMPP จาก https://www.apachefriends.org/
  2. ติดตั้งตามขั้นตอนบนหน้าจอ
  3. เปิดโปรแกรม XAMPP Control Panel
  4. คลิกปุ่ม "Start" ที่ MySQL เพื่อเริ่มเซิร์ฟเวอร์ฐานข้อมูล

ขั้นตอนที่ 2: สร้างฐานข้อมูล

  1. คลิกปุ่ม "Admin" ที่ MySQL ใน XAMPP Control Panel (จะเปิด phpMyAdmin)
  2. คลิกแท็บ "New" หรือ "สร้างฐานข้อมูลใหม่"
  3. ตั้งชื่อฐานข้อมูล: rest_api_db
  4. เลือก Collation: utf8mb4_unicode_ci
  5. คลิกปุ่ม "Create"

ขั้นตอนที่ 3: สร้างตาราง items

รัน 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 = วันที่แก้ไขล่าสุด (อัปเดตอัตโนมัติ)

ขั้นตอนที่ 4: ติดตั้ง MySQL Driver สำหรับ Node.js

เปิด Terminal ที่โฟลเดอร์โปรเจค แล้วรันคำสั่ง:

npm install mysql2

mysql2 คือ MySQL client สำหรับ Node.js ที่รองรับ Promise และมีประสิทธิภาพสูง


11.2 การเชื่อมต่อกับ MySQL

สร้างไฟล์ใหม่ชื่อ server-mysql.js สำหรับเชื่อมต่อกับ MySQL:

แบบที่ 1: การเชื่อมต่อแบบพื้นฐาน

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}`);
});

แบบที่ 2: การใช้ Connection Pool (แนะนำ)

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:

  • จัดการการเชื่อมต่อได้มีประสิทธิภาพมากกว่า
  • รองรับ Request พร้อมกันหลายๆ รายการได้
  • ลดเวลาในการสร้างการเชื่อมต่อใหม่
  • ป้องกันปัญหา "Too many connections"

11.3 CRUD Operations กับ MySQL

ตัวอย่างโค้ดเต็มรูปแบบของ REST API ที่เชื่อมต่อกับ MySQL:

11.3.1 GET - ดึงข้อมูลทั้งหมด

// 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

11.3.2 GET - ดึงข้อมูลตาม ID

// 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]);

11.3.3 POST - สร้างข้อมูลใหม่

// 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 ของแถวที่เพิ่งถูก Insert
  • result.affectedRows = จำนวนแถวที่ถูกกระทบ
  • ใช้ status code 400 สำหรับ Validation Error
  • ใช้ status code 201 สำหรับสร้างข้อมูลสำเร็จ

11.3.4 PUT - แก้ไขข้อมูล

// 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: 'เกิดข้อผิดพลาดในการแก้ไขข้อมูล' });
    }
});

11.3.5 DELETE - ลบข้อมูล

// 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: 'เกิดข้อผิดพลาดในการลบข้อมูล' });
    }
});

11.4 ไฟล์ server-mysql.js เต็มรูปแบบ

โค้ดสำเร็จรูปทั้งหมด:

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}`);
});

11.5 การรันโปรแกรม

ขั้นตอนการรัน:

  1. ตรวจสอบว่า MySQL ใน XAMPP เปิดอยู่
  2. เปิด Terminal ที่โฟลเดอร์โปรเจค
  3. รันคำสั่ง: node server-mysql.js
  4. ทดสอบ API ด้วย Postman หรือ Browser

11.6 การแก้ปัญหาที่พบบ่อย

Q: Error: ER_BAD_DB_ERROR: Unknown database 'rest_api_db'
A: ยังไม่ได้สร้างฐานข้อมูล ให้ไปสร้างฐานข้อมูลใน phpMyAdmin ตามขั้นตอนที่ 11.1
Q: Error: connect ECONNREFUSED 127.0.0.1:3306
A: MySQL ใน XAMPP ยังไม่เปิด ให้เปิด XAMPP Control Panel แล้วกดปุ่ม Start ที่ MySQL
Q: Error: ER_ACCESS_DENIED_ERROR
A: Username หรือ Password ไม่ถูกต้อง ตรวจสอบการตั้งค่าในส่วน createPool()
Q: พอร์ต 3306 ถูกใช้งานแล้ว
A: มีโปรแกรมอื่นใช้พอร์ต MySQL อยู่ ให้ปิดโปรแกรมนั้น หรือเปลี่ยนพอร์ตใน XAMPP

11.7 Best Practices

แนวทางปฏิบัติที่ดี:

  1. ใช้ Connection Pool แทน single connection
  2. ใช้ Prepared Statements เพื่อป้องกัน SQL Injection
  3. Validate ข้อมูล ก่อนบันทึกลงฐานข้อมูล
  4. จัดการ Error ให้เหมาะสมด้วย try...catch
  5. ใช้ async/await แทน callback เพื่อโค้ดอ่านง่าย
  6. Log Error เพื่อง่ายต่อการ Debug
  7. ปิดการเชื่อมต่อ เมื่อไม่ใช้งาน (Pool จัดการให้อัตโนมัติ)
  8. แยกไฟล์ Config สำหรับการตั้งค่าฐานข้อมูล

11.8 ตัวอย่างการแยกไฟล์ Config

สร้างไฟล์ 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();

แบบฝึกหัดเพิ่มเติม

  1. เพิ่ม Endpoint สำหรับค้นหาข้อมูลตามชื่อ: GET /api/items/search?name=keyword
  2. เพิ่มตาราง categories และสร้าง Relationship กับ items
  3. เพิ่ม Pagination สำหรับ GET /api/items (limit และ offset)
  4. สร้าง API สำหรับการจัดการ Users พร้อม Authentication
  5. เพิ่มฟังก์ชัน Soft Delete (ไม่ลบจริง แต่ทำเครื่องหมายว่าลบแล้ว)

12. เคล็ดลับการเรียนรู้

Q: เหตุใดจึงควรใช้ Express.js
A: Express.js ช่วยให้เขียนโค้ดสร้าง Server ได้ง่ายและรวดเร็วกว่าการใช้ Node.js แบบ Pure โดยมี Features มากมายที่ช่วยในการพัฒนา เช่น Routing, Middleware, Template Engine
Q: ข้อมูลจะหายเมื่อปิดเซิร์ฟเวอร์หรือไม่
A: ใช่ เนื่องจากข้อมูลถูกเก็บใน Array ในหน่วยความจำ (RAM) เมื่อปิดโปรแกรมข้อมูลจะหายไป หากต้องการเก็บข้อมูลถาวรจำเป็นต้องใช้ฐานข้อมูล
Q: พอร์ต 3000 ถูกใช้งานแล้ว จะแก้ไขอย่างไร
A: สามารถเปลี่ยนเป็นพอร์ตอื่นได้ เช่น 3001, 8080, 5000 โดยแก้ไขค่า PORT ในโค้ด
Q: จะทดสอบ API ได้อย่างไร
A: มีหลายวิธี เช่น ใช้โปรแกรม Postman, Insomnia, Thunder Client (VS Code Extension) หรือใช้คำสั่ง curl ใน Command Line
Q: ความแตกต่างระหว่าง PUT และ PATCH คืออะไร
A: PUT ใช้สำหรับอัพเดตข้อมูลทั้งหมด ส่วน PATCH ใช้สำหรับอัพเดตเฉพาะบางส่วน ในโค้ดนี้ใช้ PUT

14. แบบฝึกหัด

แบบฝึกหัดที่ 1: เพิ่ม Field ใหม่

โจทย์: เพิ่มฟิลด์ price (ราคา) และ quantity (จำนวน) ให้กับ item

คำแนะนำ:

  • แก้ไข array items ให้มี property เพิ่มเติม
  • แก้ไข POST และ PUT endpoints ให้รับค่าเพิ่มเติม

แบบฝึกหัดที่ 2: Validation

โจทย์: เพิ่มการตรวจสอบข้อมูลใน POST endpoint

เงื่อนไข:

  • name ต้องไม่เป็นค่าว่าง
  • description ต้องมีความยาวอย่างน้อย 5 ตัวอักษร
  • หากไม่ผ่านเงื่อนไข ส่ง Status Code 400 พร้อมข้อความ Error

แบบฝึกหัดที่ 3: สร้าง API ใหม่

โจทย์: สร้าง API สำหรับจัดการข้อมูล Users

โครงสร้างข้อมูล:

  • id (ตัวเลข)
  • username (ข้อความ)
  • email (ข้อความ)
  • age (ตัวเลข)

Endpoints ที่ต้องสร้าง:

  • GET /api/users - ดึงข้อมูล users ทั้งหมด
  • GET /api/users/:id - ดึงข้อมูล user ตาม id
  • POST /api/users - สร้าง user ใหม่
  • PUT /api/users/:id - แก้ไขข้อมูล user
  • DELETE /api/users/:id - ลบ user

แบบฝึกหัดที่ 4: Search และ Filter

โจทย์: เพิ่ม endpoint สำหรับค้นหา items ตามชื่อ

ตัวอย่าง:

GET /api/items/search?name=Item 1

คำแนะนำ: ใช้ req.query.name และ filter() method


15. แหล่งข้อมูลเพิ่มเติม