Python Back-End Development for Beginners
บทความโดย ผศ.ดร.ณัฐโชติ พรหมฤทธิ์
ภาควิชาคอมพิวเตอร์
คณะวิทยาศาสตร์
มหาวิทยาลัยศิลปากร
บทความนี้ผู้อ่านจะได้พัฒนา REST API สำหรับ User Service ด้วย Flask ซึ่งเป็น Web Framework ขนาดเล็กใน Python โดยมี API Endpoint ดังต่อไปนี้
- GET /api/v1/users
- GET /api/v1/users/{user_id}
- POST /api/v1/users
- PUT /api/v1/users/{user_id}
- DELETE /api/v1/users/{user_id}
- GET /health
เราจะใช้ HTTP Verb (Method) หลัก ๆ ได้แก่ GET, POST, PUT และ DELETE เพื่อกําหนดการดําเนินการที่เราสามารถทำได้กับ Resource ของ User Service
โดย GET จะถูกใช้เพื่อดึง Resource และ POST จะถูกใช้สำหรับสร้าง Resource ใหม่ ส่วน PUT ถูกใช้สำหรับการ Update แบบแทนที่ และ DELETE สำหรับลบ Resource ที่มีอยู่
แต่ละ Endpoint มีรายละเอียดดังต่อไปนี้
GET /api/v1/users
- สำหรับดึงข้อมูลผู้ใช้ทั้งหมดจากฐานข้อมูล เรียงตาม id จากน้อยไปมาก
- ไม่ต้องส่ง Parameter
- ต้องมี Bearer Token ใน Header
- ส่งคืนข้อมูลเป็น Array ของผู้ใช้
@app.route('/api/v1/users', methods=['GET'])
def get_users():
# ดึงข้อมูลผู้ใช้ทั้งหมดจากฐานข้อมูล
# เรียงตามไอดี จากน้อยไปมาก
query = "SELECT * FROM users ORDER BY id"
# ตัวอย่างผลลัพธ์:
[
{
"id": 1,
"name": "Nuttachot",
"email": "nuttachot@email.com"
},
{
"id": 2,
"name": "Poohkan",
"email": "poohkan@email.com"
}
]
GET /api/v1/users/{user_id}
- สำหรับดึงข้อมูลผู้ใช้ตาม ID ที่ระบุ
- ต้องระบุ user_id ที่ต้องการใน Path
- ต้องมี Bearer Token ใน Header
- ถ้าไม่พบผู้ใช้จะ Return 404
@app.route('/api/v1/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
# ดึงข้อมูลผู้ใช้ตาม ID ที่ระบุ
query = "SELECT * FROM users WHERE id = %s"
# ตัวอย่างผลลัพธ์:
{
"id": 1,
"name": "Nuttachot",
"email": "nuttachot@email.com"
}
POST /api/v1/users
- สำหรับสร้างผู้ใช้ใหม่จากข้อมูลที่ส่งมา
- ต้องส่งข้อมูล name และ email มาในรูปแบบ JSON
- ต้องมี Bearer Token ใน Header
- email ต้องไม่ซ้ำกับที่มีอยู่แล้ว
- ส่งคืนข้อมูลผู้ใช้ที่สร้างใหม่พร้อม ID
@app.route('/api/v1/users', methods=['POST'])
def create_user():
# สร้างผู้ใช้ใหม่จากข้อมูลที่ส่งมา
# ตัวอย่างข้อมูลที่ต้องส่ง:
{
"name": "Nuttachot",
"email": "nuttachot@email.com"
}
# ตัวอย่างผลลัพธ์:
{
"id": 1, # ID จะถูกสร้างอัตโนมัติ
"name": "Nuttachot",
"email": "nuttachot@email.com"
}
PUT /api/v1/users/{user_id}
- สำหรับอัพเดทข้อมูลผู้ใช้ทั้งหมดตาม ID ที่ระบุ
- ต้องระบุ user_id ที่ต้องการแก้ไขใน Path
- ต้องส่งข้อมูลใหม่ทั้ง name และ email
- ต้องมี Bearer Token ใน header
- ถ้าไม่พบผู้ใช้จะ Return 404
- email ใหม่ต้องไม่ซ้ำกับคนอื่น
- ส่งคืนข้อมูลผู้ใช้ที่อัพเดทแล้ว
@app.route('/api/v1/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
# อัพเดทข้อมูลผู้ใช้ตาม ID ที่ระบุ
# ตัวอย่างข้อมูลที่ต้องส่ง:
{
"name": "Nuttachot Promrit",
"email": "nuttachot.new@email.com"
}
# ตัวอย่างผลลัพธ์:
{
"id": 1,
"name": "Nuttachot Promrit",
"email": "nuttachot.new@email.com"
}
DELETE /api/v1/users/{user_id}
- สำหรับลบผู้ใช้ตาม ID ที่ระบุ
- ต้องระบุ user_id ที่ต้องการลบใน Path
- ต้องมี Bearer Token ใน Header
- ถ้าไม่พบผู้ใช้จะ Return 404
- ส่งคืนข้อความยืนยันการลบสำเร็จ
@app.route('/api/v1/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
# ลบผู้ใช้ตาม ID ที่ระบุ
# ตัวอย่างผลลัพธ์:
{
"message": "User deleted successfully"
}
GET /health
- สำหรับตรวจสอบการเชื่อมต่อกับฐานข้อมูล
- ไม่ต้องใช้ Bearer Token
@app.route('/health', methods=['GET'])
def health_check():
# ตรวจสอบการเชื่อมต่อกับฐานข้อมูล
# ตัวอย่างผลลัพธ์ (กรณีปกติ):
{
"status": "healthy",
"database": "connected"
}
# ตัวอย่างผลลัพธ์ (กรณีมีปัญหา):
{
"detail": "Database connection failed: error message"
}
Project นี้จะมีการพัฒนาบน Github Codespaces และ VS Code Editor ซึ่งเป็น Linux-based Environment สำหรับนักพัฒนาที่ง่ายในการทดลอง Deploy API และ Database Server บน Docker Container โดยจะมีการสร้าง Git Repository 3 Repo บน Github ได้แก่
- userservice Repo สำหรับเก็บ Codebase ของ REST API
- userdatabase Repo สำหรับเก็บ Codebase ของการ Config PostgreSQL
- backend Repo สำหรับการสร้าง Codespace
นอกจากนี้ยังมีรูปแบบในการจัดการ Branch ใน Git ที่มีลักษณะการพัฒนา Code แบบรวมศูนย์บน Branch หลัก (main Branch) ที่เรียกว่า Trunk-Based Development ซึ่งหลาย ๆ บริษัท เช่น Google, Facebook, Netflix, และ Amazon นำมาปรับใช้เพื่อให้สามารถพัฒนาและ Deploy Codeใหม่ ๆ ได้อย่างต่อเนื่องและรวดเร็ว
โดยนักพัฒนาทุกคนในทีมจะทำงานและ Merge Code เข้ามายัง Trunk โดยตรงอย่างสม่ำเสมอ ซึ่งต่างจาก Git flow ทั่วไปที่อาจมี Branch หลายระดับ เช่น main, develop หรือ feature Branch
ดังนั้นบนความนี้ไม่ใช่มีเนื้อหาเฉพาะการพัฒนา REST API เท่านั้น แต่ยังแสดงตัวอย่าง Workflow ของการพัฒนา Software แบบสมัยใหม่ที่เน้นการทำงานร่วมกันเป็นทีมด้วยแนวทางแบบ Trunk-Based Development ซึ่งผู้พัฒนาจะได้ใช้ VS Code ร่วมกับ Github Codespaces ในการทดสอบการ Deploy Software บน Linux-based Environment ก่อนนำขึ้น Production ต่อไป
สร้าง Github Codespaces
Codespaces เป็น Platform ที่ให้บริการนักพัฒนา Software ในรูปแบบของ Cloud-based จาก GitHub โดยเราสามารถสร้าง Environment ที่มีเครื่องมือและ Resource พร้อมใช้งานสำหรับการพัฒนา Software ได้อย่างรวดเร็ว นักพัฒนาสามารถสร้าง Codespace ที่เป็น Linux-based OS และเครื่องมือต่าง ๆ เช่น Git, Docker และ Python เป็นต้น
- ไปยัง github.com แล้ว Login
- กด New เพื่อสร้าง Repository ตั้งชื่อเป็น backend เลือกชนิด Repo แบบ Private และเลือก Add a README file แล้วกด Create repository
- เลือกเมนู Codespace
- กด New Codespace
- ที่หน้าสร้าง Codespace เลือก Repository เป็น backend จากที่สร้างไว้ แล้วเลือก Region และ Machine type ตามที่ต้องการ แล้วกด Create codespace
- Codespace จะเปิด VS Code บน Browser ให้ทำงาน ดู Version ของ OS ด้วยคำสั่งต่อไปนี้
cat /etc/os-release
เพื่อการใช้งาน Codespace และทดสอบ Code ที่ Seamless ขึ้น เหมือนการจำลอง Environment dev มาไว้บน Localhost เราจะรัน VS Code จากเครื่องเราเองโดยไม่ใช้ Browser แต่ก่อนอื่นให้ปิดหน้าต่าง VS Code บน Browser ก่อน ป้องกันการลืมปิด ซึ่ง Github จะนับจำนวนชั่วโมงการให้ Resource เราตาม Quota ที่มี (มีทั้งแบบ Free Plan และ GitHub Pro)
- ไปที่เมนู Codespace อีกครั้ง แล้วกดที่ปุ่ม 3 จุด
- เลือก Open in Visual Studio Code
- กด Allow
- เลือก Open
- ดู Version ของ Git และ Docker ด้วยคำสั่งต่อไปนี้
git version
docker version
Config PostgreSQL และ Deploy บน Docker Container
เราจะใช้แนวทางในการพัฒนา Software แบบ Trunk-Based Development (TBD) ซึ่งมีหลักการสำคัญ คือ
- การ Commit และ Merge บ่อย ๆ นักพัฒนาจะ Commit และ Merge การเปลี่ยนแปลงของตัวเองเข้ากับ Trunk (Branch หลัก) บ่อยครั้ง อาจจะเป็นรายวันหรือบ่อยกว่านั้น การรวม Code บ่อย ๆ ช่วยให้ทีมทำงานร่วมกันได้อย่างคล่องตัวและลดโอกาสการเกิด Merge Conflicts ขนาดใหญ่
- ขนาดงานที่เล็กและแตก Branch สั้น ๆ ใน TBD งานแต่ละชิ้นควรจะเล็กพอที่จะทำเสร็จได้ภายในระยะสั้น ๆ นักพัฒนาอาจแตก Branch ย่อยขึ้นมาเพื่อทำงานในบางงาน แต่ Branch ย่อยนั้นควรจะใช้เวลาไม่นานก่อนจะ Merge กลับไปยัง Trunk ไม่มี Branch ระยะยาว
โดยเราจะสร้าง userdatabase Repo สำหรับเก็บ Codebase ของการ Config PostgreSQL ที่มีโครงสร้าง Project ดังต่อไปนี้
.
├── README.md
├── backup
│ ├── Dockerfile
│ └── backup.sh
├── backups
├── docker
│ ├── Dockerfile
│ └── init.sql
├── .env
├── .gitignore
└── docker-compose.yml
- สร้าง Folder userdatabase เข้าไปใน Folder นี้แล้วเริ่มต้นใช้งาน Git ด้วยคำสั่งต่อไปนี้
git init
- ไปที่ Github.com สร้าง Git Repo ชื่อ userdatabase เลือกชนิด Repo แบบ Private แล้วกด Create repository
- เลือก SSH แล้วกด Copy URL
- เชื่อมต่อ Codespace กับ Git Repo ด้วยคำสั่ง git remote add origin ตามด้วย URL ที่ได้ Copy มา เช่น
git remote add origin git@github.com:promritn/userdatabase.git
- สร้าง SSH Key บน Codespace สำหรับการ Push และ Pull Code กับ Git Repo ที่ได้เชื่อมต่อ โดยไม่ต้องป้อน Username และ Password ทุกครั้ง
สร้าง ssh-keygen
ssh-keygen -t ed25519 -C "your_email@example.com"
แสดง public key บน Linux
cat ~/.ssh/id_ed25519.pub
- ใช้คำสั่ง cat ~/.ssh/id_ed25519.pub เพื่อแสดง Public Key ดังเช่นตัวอย่างต่อไปนี้แล้ว Copy ไว้
ssh-ed25519 ABCAC3NzaC1lZFI1NTE3AAAAIMmPOcXyJu+c/2Ork3pmgBU9FBl1iwxBr97Bh1MxI6sB nuttachot@hotmail.com
- นำ Public Key ไปเพิ่มบน Github Repo โดยไปที่รูป Profile เลือก Your organizations เลือกเมนู SSH and GPG keys แล้วกด New SSH key
- ตั้งชื่อ userdatabase และนำ Public key ที่ Copy ไปวาง แล้วกด Add SSH key
- ยืนยันตัวตน
- กลับมาที่ Codespace สร้างไฟล์ README.md ใน Folder userdatabase
touch README.md
- พิมพ์หัวข้อ PostgreSQL config แบบหัวเรื่องระดับ 1 ในไฟล์ README.md ด้วย Tag ของ Markdown แล้ว Save
# PostgreSQL config
- Commit เข้า Git ด้วยคำสั่งต่อไปนี้
git add .
git commit -m 'first commit'
- Push Code ที่ Commit ขึ้น Github Server
git push origin main
- ไปที่ Browser แล้ว Refresh หน้าต่าง userdatabase Repo จะเห็นหัวข้อ PostgreSQL config แบบตัวใหญ่
เราจะ Config PostgreSQL บน Branch ย่อย ซึ่งตามหลักการของ Trunk-Based Developmen แล้วนักพัฒนาควรจะสร้าง Branch ย่อยเฉพาะงานของตัวเอง (Short-Lived Branch) จาก Trunk ซึ่งงานควรมีขนาดเล็กและสามารถทำให้เสร็จได้ภายในเวลาอันสั้น (ไม่เกิน 1 วัน) เพื่อให้ง่ายต่อการ Merge กลับไปยัง Trunk อย่างรวดเร็ว
- สร้าง Short-Lived Branch ชื่อ feature/config-postgresql
git checkout -b feature/config-postgresql
- สร้างไฟล์และ Folder ของ Project ตามโครงสร้างดังต่อไปนี้
.
├── README.md
├── backup
│ ├── Dockerfile
│ └── backup.sh
├── backups
├── docker
│ ├── Dockerfile
│ └── init.sql
├── .env
├── .gitignore
└── docker-compose.yml
- แก้ไขไฟล์ docker-compose.yml สำหรับการ Deploy PosgreSQL, PGAdmin และ Backup Container
- แก้ไขไฟล์ .env
- แก้ไขไฟล์ .gitignore
- แก้ไขไฟล์ Dockerfile ใน Folder docker
- แก้ไขไฟล์ init.sql ใน Folder docker
- แก้ไขไฟล์ Dockerfile ใน Folder backup
- แก้ไขไฟล์ backup.sh ใน Folder backup
#!/bin/bash
# backup/backup.sh
# กำหนดตัวแปร
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_${TIMESTAMP}"
# สร้างโฟลเดอร์ backup ถ้ายังไม่มี
mkdir -p ${BACKUP_DIR}
# ทำ Database Backup
echo "Starting backup of PostgreSQL database: ${POSTGRES_DB}"
PGPASSWORD=${POSTGRES_PASSWORD} pg_dump -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -d ${POSTGRES_DB} -F c -b -v -f "${BACKUP_DIR}/${BACKUP_FILE}.backup"
# บีบอัดไฟล์ backup
echo "Compressing backup file..."
pigz "${BACKUP_DIR}/${BACKUP_FILE}.backup"
# ลบไฟล์ backup เก่า
echo "Removing old backups..."
find ${BACKUP_DIR} -type f -name "*.backup.gz" -mtime +${BACKUP_RETENTION_DAYS} -delete
# ตรวจสอบสถานะการทำงาน
if [ $? -eq 0 ]; then
echo "Backup completed successfully: ${BACKUP_FILE}.backup.gz"
else
echo "Backup failed!"
exit 1
fi
# สร้าง symlink ไปยัง backup ล่าสุด
ln -sf "${BACKUP_DIR}/${BACKUP_FILE}.backup.gz" "${BACKUP_DIR}/latest.backup.gz"
ก่อน Commit Code เข้า Git นักพัฒนาต้องทดสอบก่อนว่ามันทำงานได้
- Deploy PostgreSQL, PGAdmin และ Backup Container
docker-compose up -d
- ตรวจสอบ Container ที่ Deploy
docker-compose ps
- ดู Logs ของ 3 Container ที่รัน
docker-compose logs
- ทดลอง Backup Database
docker exec postgres_backup /backup.sh
- สำหรับการ Restore เราจะใช้คำสั่งดังต่อไปนี้
# Latest backup
docker exec -it user_postgres pg_restore -U [user] -d [dbname] -v /backups/latest.backup.gz
# Specific backup
docker exec -it user_postgres pg_restore -U [user] -d [dbname] -v /backups/backup_[timestamp].backup.gz
ทดลอง Query ข้อมูลผ่าน PG Admin ดังนี้
- ไปยัง URL http://localhost:5050 ใส่ Username และ Password ตามที่ได้กำหนดไว้ในไฟล์ .env
- กด Add New Server
- ตั้งชื่อ Connection
- กำหนดค่า Connection ต่างๆ
- คลิ๊กขวาที่ postgres เลือก Query Tool
- ทดสอบ Query ข้อมูลใน Table users ด้วยคำสั่งต่อไปนี้ แล้วกดปุ่ม Play
select * from users
- เมื่อทดสอบการ Deploy, Backup Database และ Query ข้อมูลแล้วจึง Commit Code เข้า Git ด้วยคำสั่งต่อไปนี้
git add .
git commit -m 'config postgresql, backup container and pgadmin'
- ดู History ที่ Commit
git log --oneline
Rebase โดยนำโค้ดใน branch ของเรามาอัปเดตให้ตรงกับ Trunk ล่าสุด
- แต่ก่อน Rebase ควรดึงการเปลี่ยนแปลงล่าสุดจาก Trunk เพื่อให้แน่ใจว่า Branch ของเราอยู่ในสถานะล่าสุด เหมือนบน Github Server
git checkout main
git pull origin main
- Rebase เพื่อนำโค้ดใน Branch ของเรามา Update ให้ตรงกับ Trunk ล่าสุด
git checkout feature/config-postgresql
git rebase main
- เมื่อ Code ใน Branch feature/config-postgresql ตรงกับ Trank ล่าสุดแล้ว จึง Merge กลับไปยัง Trunk (ถ้ามี Conflict ตอน Rebase ให้แก้ไข Conflict ก่อน Merge)
git checkout main
git merge feature/config-postgresql
*อาจ Push Branch feature/config-postgresql ขึ้น Github Server เพื่อเปิด Pull Request (PR) ให้ทีมตรวจสอบก่อนก็ได้
**ขั้นตอนการแก้ไข Conflict หลังจาก Rebase
1. ดูไฟล์ที่เกิด Conflict
2. เปิดไฟล์ที่มี Conflict และแก้ไขความขัดแย้ง
3. บันทึกไฟล์ที่แก้ไขแล้ว
4. ใช้ git add กับไฟล์ที่แก้ไข
5. ใช้ git rebase --continue เพื่อดำเนินการ Rebase ต่อ
6. หากยังมี Conflict ให้ทำซ้ำขั้นตอนจนกว่าจะเสร็จสิ้น
ยกเลิกการ Rebase (ถ้าจำเป็น)
git rebase --abort
- Push Code ขึ้น Github Server
git push origin main
- ลบ Branch ย่อยที่เสร็จแล้ว
git branch -d feature/config-postgresql
- ดู History ทั้งหมด
git log --oneline
Trunk ของเราจะมี History เป็นเส้นตรงสวยงาม
พัฒนา API เชื่อมต่อกับ PostgreSQL และ Deploy บน Docker Container
เราจะสร้าง userservice Repo สำหรับเก็บ Codebase ของ REST API โดยมีโครงสร้างของ Project ดังต่อไปนี้
.
├── .env
├── .gitignore
├── Dockerfile
├── README.md
├── docker-compose.yml
├── main.py
└── requirements.txt
- สร้าง Folder userservice เข้าไปใน Folder นี้แล้วเริ่มต้นใช้งาน Git ด้วยคำสั่งต่อไปนี้
git init
- ไปที่ Github.com สร้าง Git Repo ชื่อ userservice เลือกชนิด Repo แบบ Private แล้วกด Create repository
- เลือก SSH แล้วกด Copy URL
- เชื่อมต่อ Codespace กับ Git Repo ด้วยคำสั่ง git remote add origin ตามด้วย URL ที่ได้ Copy มา เช่น
git remote add origin git@github.com:promritn/userservice.git
- สร้างไฟล์ README.md ใน Folder userservice
touch README.md
- พิมพ์หัวข้อ REST API Project แบบหัวเรื่องระดับ 1 ในไฟล์ README.md ด้วย Tag ของ Markdown แล้ว Save
# REST API Project
- Commit เข้า Git ด้วยคำสั่งต่อไปนี้
git add .
git commit -m 'first commit'
- Push Code ที่ Commit ขึ้น Github Server
git push origin main
สร้าง Short-Lived Branch ชื่อ feature/restapi-dev
git checkout -b feature/restapi-dev
- สร้างไฟล์และ Folder ของ Project ตามโครงสร้างดังต่อไปนี้
.
├── .env
├── .gitignore
├── Dockerfile
├── README.md
├── docker-compose.yml
├── main.py
└── requirements.txt
- แก้ไขไฟล์ docker-compose.yml
- แก้ไขไฟล์ .env
- แก้ไขไฟล์ .gitignore
- แก้ไขไฟล์ Dockerfile
FROM python:3.11-slim
WORKDIR /app
# ติดตั้ง system dependencies และ curl สำหรับ health check
RUN apt-get update && apt-get install -y \
gcc \
libpq-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# คัดลอกไฟล์ requirements.txt
COPY requirements.txt .
# ติดตั้ง Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# คัดลอกโค้ดทั้งหมด
COPY . .
# แก้ปัญหา permission denied สำหรับ non-root user
RUN useradd -m myuser
RUN chown -R myuser:myuser /app
USER myuser
# รัน Flask ด้วย gunicorn
CMD ["gunicorn", "-b", "0.0.0.0:8000", "main:app", "--access-logfile", "-", "--error-logfile", "-"]
- แก้ไข main.py
from flask import Flask, request, jsonify, abort
from flask_httpauth import HTTPTokenAuth
from flask_cors import CORS
import psycopg2
import psycopg2.extras
import os
# ดึงค่า config จาก environment variables
DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")
DB_NAME = os.getenv("DB_NAME")
DB_HOST = os.getenv("DB_HOST", "localhost") # ค่าเริ่มต้นคือ localhost
DB_PORT = os.getenv("DB_PORT", "5432") # ค่าเริ่มต้นคือ 5432
API_TOKEN = os.getenv("API_TOKEN")
# สร้าง Flask app
app = Flask(__name__)
# ตั้งค่า CORS
CORS(app, resources={r"/api/v1/*": {"origins": "http://localhost:3000"}})
# สร้าง authentication instance
auth = HTTPTokenAuth(scheme='Bearer')
# ฟังก์ชันตรวจสอบ token
@auth.verify_token
def verify_token(token):
return token == API_TOKEN
# ฟังก์ชันสำหรับเชื่อมต่อฐานข้อมูล
def get_db_connection():
conn = psycopg2.connect(
host=DB_HOST,
port=DB_PORT,
dbname=DB_NAME,
user=DB_USER,
password=DB_PASSWORD
)
return conn
# สร้าง API Blueprint สำหรับ version 1
from flask import Blueprint
api_v1 = Blueprint('api_v1', __name__, url_prefix='/api/v1')
# Routes ภายใต้ Blueprint api_v1
@api_v1.route('/users', methods=['GET'])
@auth.login_required
def get_users():
"""ดึงข้อมูลผู้ใช้ทั้งหมด"""
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor.execute('SELECT * FROM users')
users = cursor.fetchall()
cursor.close()
conn.close()
users_list = [dict(user) for user in users]
return jsonify(users_list)
@api_v1.route('/users/<int:user_id>', methods=['GET'])
@auth.login_required
def get_user(user_id):
"""ดึงข้อมูลผู้ใช้ตาม ID"""
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))
user = cursor.fetchone()
cursor.close()
conn.close()
if not user:
abort(404, description="ไม่พบผู้ใช้")
return jsonify(dict(user))
@api_v1.route('/users', methods=['POST'])
@auth.login_required
def create_user():
"""สร้างผู้ใช้ใหม่"""
data = request.get_json()
name = data.get('name')
email = data.get('email')
if not name or not email:
return jsonify({'error': 'Name and email are required'}), 400
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
try:
cursor.execute(
'INSERT INTO users (name, email) VALUES (%s, %s) RETURNING *',
(name, email)
)
new_user = cursor.fetchone()
conn.commit()
except psycopg2.errors.UniqueViolation:
conn.rollback()
cursor.close()
conn.close()
return jsonify({'error': 'อีเมลนี้ถูกใช้งานแล้ว'}), 400
except Exception as e:
conn.rollback()
cursor.close()
conn.close()
abort(500, description="เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์")
cursor.close()
conn.close()
return jsonify(dict(new_user)), 201
@api_v1.route('/users/<int:user_id>', methods=['PUT'])
@auth.login_required
def update_user(user_id):
"""อัพเดทข้อมูลผู้ใช้"""
data = request.get_json()
name = data.get('name')
email = data.get('email')
if not name or not email:
return jsonify({'error': 'Name and email are required'}), 400
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))
user = cursor.fetchone()
if not user:
cursor.close()
conn.close()
abort(404, description="ไม่พบผู้ใช้")
try:
cursor.execute(
'UPDATE users SET name = %s, email = %s WHERE id = %s RETURNING *',
(name, email, user_id)
)
updated_user = cursor.fetchone()
conn.commit()
except psycopg2.errors.UniqueViolation:
conn.rollback()
cursor.close()
conn.close()
return jsonify({'error': 'อีเมลนี้ถูกใช้งานแล้ว'}), 400
except Exception as e:
conn.rollback()
cursor.close()
conn.close()
abort(500, description="ไม่สามารถอัพเดทข้อมูลได้")
cursor.close()
conn.close()
return jsonify(dict(updated_user))
@api_v1.route('/users/<int:user_id>', methods=['DELETE'])
@auth.login_required
def delete_user(user_id):
"""ลบผู้ใช้"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))
user = cursor.fetchone()
if not user:
cursor.close()
conn.close()
abort(404, description="ไม่พบผู้ใช้")
try:
cursor.execute('DELETE FROM users WHERE id = %s', (user_id,))
conn.commit()
except Exception as e:
conn.rollback()
cursor.close()
conn.close()
abort(500, description="เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์")
cursor.close()
conn.close()
return jsonify({"message": "ลบผู้ใช้สำเร็จ"})
# Health check route ที่ระดับ root (นอก blueprint)
@app.route('/health', methods=['GET'])
def health_check():
"""ตรวจสอบสถานะของ API และการเชื่อมต่อฐานข้อมูล"""
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT 1')
cursor.fetchone()
cursor.close()
conn.close()
return jsonify({"status": "healthy", "database": "connected"})
except Exception:
abort(503, description="Database connection failed")
# ลงทะเบียน Blueprint
app.register_blueprint(api_v1)
แก้ไข requirement.txt
# requirements.txt
Flask==2.3.2
Flask-HTTPAuth==4.8.0
Flask-CORS==3.0.10
psycopg2-binary==2.9.7
python-dotenv==1.0.1
gunicorn==23.0.0
ก่อน Commit Code เข้า Git นักพัฒนาต้องทดสอบก่อนว่ามันทำงานได้
- Deploy API
docker-compose up -d
- ตรวจสอบ Container ที่ Deploy
docker-compose ps
- ดู Logs ของ Container ที่รัน
docker-compose logs
- ทดลองยิง API เส้น /health ผ่าน curl
curl http://localhost:8000/health
- ทดลองยิง API เส้น /health ผ่าน Postman
- ทดลองดึงข้อมูล users ทั้งหมดจาก Database ผ่าน Postman ด้วย Method GET (ต้องมี Bearer Token ใน Header)
http://localhost:8000/api/v1/users
- ทดลองดึงข้อมูล users คนที่ 1 จาก Database ผ่าน Postman ด้วย Method GET (ต้องมี Bearer Token ใน Header)
http://localhost:8000/api/v1/users/1
- ทดลองเพิ่ม users ใหม่ลง Database โดยรับข้อมูลแบบ JSON Format ผ่าน Postman ด้วย Method POST (ต้องมี Bearer Token ใน Header)
http://localhost:8000/api/v1/users
- ทดลองแก้ไข Email ของ User คนที่ 1 เป็น nuttachot@hotmail.com ผ่าน JSON Format ด้วย Method PUT (ต้องมี Bearer Token ใน Header)
http://localhost:8000/api/v1/users/1
{
"name": "nuttachot promrit",
"email": "nuttachot@hotmail.com"
}
- ทดลองลบ User คนที่ 3 ด้วย Method DELETE (ต้องมี Bearer Token ใน Header)
http://localhost:8000/api/v1/users/3
- เมื่อทดสอบการยิง API แต่ละเส้นแล้วจึง Commit Code เข้า Git ด้วยคำสั่งต่อไปนี้
git add .
git commit -m 'rest api dev'
ดู History ที่ Commit
git log --oneline
Rebase โดยนำโค้ดใน Branch ของเรามา Update ให้ตรงกับ Trunk ล่าสุด
- แต่ก่อน Rebase ควรดึงการเปลี่ยนแปลงล่าสุดจาก Trunk เพื่อให้แน่ใจว่า Branch ของเราอยู่ในสถานะล่าสุด เหมือนบน Github Server
git checkout main
git pull origin main
- Rebase เพื่อนำโค้ดใน Branch ของเรามา Update ให้ตรงกับ Trunk ล่าสุด
git checkout feature/restapi-dev
git rebase main
- เมื่อ Code ใน Branch feature/restapi-dev ตรงกับ Trank ล่าสุดแล้ว จึง Merge กลับไปยัง Trunk (ถ้ามี Conflict ให้แก้ไข Conflict ก่อน Merge)
git checkout main
git merge feature/restapi-dev
- Push Code ขึ้น Github Server
git push origin main
- ลบ Branch ย่อยที่เสร็จแล้ว
git branch -d feature/restapi-dev
- ดู History ทั้งหมด
git log --oneline
Expose API ให้เข้าถึงจาก Internet
- ติดตั้ง Cloudflared
sudo apt update
wget -O cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
sudo mv cloudflared /usr/local/bin/
sudo chmod +x /usr/local/bin/cloudflared
cloudflared --version
- สร้าง Cloudflare Tunnel
cloudflared tunnel --url http://0.0.0.0:8000
- Copy URL ที่ได้นำไปเปิดบน Browser โดยเพิ่ม Path /health เช่น
https://creates-ignored-digest-filled.trycloudflare.com/health