Skip to main content

3 posts tagged with "python"

View All Tags

· 7 min read

FastAPI สู่ความสนุกและความเร็วในการสร้าง API

FastAPI เป็นเฟรมเวิร์ก Python ที่ทำให้การสร้าง API เป็นเรื่องง่ายและมีประสิทธิภาพ ด้วยการใช้งาน FastAPI, คุณสามารถพัฒนา API ที่มีประสิทธิภาพสูงและรวดเร็วได้อย่างง่ายดาย ในบทความนี้ และบทความนี้เราจะใช้ VSCODE ในการเขียนโค้ดกันนะครับ และเชื่อว่าหลายๆท่านก็คงใช้ มั้ง 😅

ติดตั้ง FastAPI และ Uvicorn

เริ่มต้นด้วยการติดตั้ง virtualenv กันก่อนแน่นอนว่าจะไปแบบเร็วๆเลย :

python -m venv _env
"venv/Scripts/activate"

มาเริ่มติดตั้ง FastAPI กัน:

pip install fastapi

แน่นอนว่าเราต้องใช้ตัวที่จะมาช่วยในการ run web server ก็คือ uvicorn:

pip insatll uvicorn

ติดตั้งเครื่องมือสำหรับยิง API กัน 🔨

  • ตัวหลักก็คือ Postman ดาวโหลดและติดตั้งฟรีได้เลยที่ https://www.postman.com/

  • ตัวต่อมาคือ https://hoppscotch.io/ เอามาเป็นตัวเลือกให้ลองใช้ไม่จำเป็นต้องโหลดสามารถใช้บนบราวเซอร์ได้เลย

สร้าง API ง่าย ๆ ⛏️

สร้างไฟล์ main.py และเขียนโค้ดต่อไปนี้:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

รัน FastAPI ด้วย Uvicorn:

uvicorn main:app
  • main คือชื่อไฟล์ main.py

  • app คือชื่อตัวแปร app = FastAPI()

หากต้องการใช้ server รีโหลดอัตโนมัติทุกครั้งที่บันทึก สามารถทำแบบนี้ได้:

uvicorn main:app --reload

แล้วจะได้เลขไอพีออกมาในลักษณะนี้ http://127.0.0.1:8000

นำไปทดสอบยิง API ด้วย Postman กัน ⛏️

กดที่เครื่องหมาย + ได้เลย:

จะมี Method ให้เลือกมากมาย ตอนนี้เราเลือก Get ตัวอื่นๆเดี๋ยวเราจะเลือกในอนาคตกัน

วาง http://127.0.0.1:8000 ลงในช่อง Enter URL text

จากนั้นกด Enter หรือปุ่ม Send ได้เลย

เราจะได้ข้อมูลที่ถูก return ออกมาแล้วตามโค้ดที่เราเขียนไว้

ประมาณนี้คร่าวๆเกี่ยวกับการใช้ Postman เราจะเขียนโค้ดกันต่อ

Method ใน FastAPI

FastAPI รองรับวิธีการ HTTP หลักๆ ดังนี้:

  • GET: ใช้สำหรับดึงข้อมูลจากเซิร์ฟเวอร์

  • POST: ใช้สำหรับส่งข้อมูลไปยังเซิร์ฟเวอร์

  • PUT: ใช้สำหรับอัปเดตข้อมูลบนเซิร์ฟเวอร์

  • PATCH: ใช้สำหรับอัปเดตบางส่วนของข้อมูลบนเซิร์ฟเวอร์

  • DELETE: ใช้สำหรับลบข้อมูลบนเซิร์ฟเวอร์

นอกจากนี้ FastAPI ยังรองรับวิธีการอื่นๆ เพิ่มเติม เช่น:

  • OPTIONS: ใช้สำหรับดึงข้อมูลเกี่ยวกับตัวเลือกที่มีสำหรับ endpoint

  • HEAD: ใช้สำหรับดึง header ของ response โดยไม่ต้องดึง body

โดยการใช้ decorator กับ method ลักษณะนี้:

  • @app.get : ใช้สำหรับ GET method

  • @app.post : ใช้สำหรับ POST method

  • @app.put : ใช้สำหรับ PUT method

  • @app.delete : ใช้สำหรับ DELETE method

  • @app.patch : ใช้สำหรับ PATCH method

  • @app.options : ใช้สำหรับ OPTIONS method

  • @app.head : ใช้สำหรับ HEAD method

Method GET

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

จากโค้ดคุณจะเห็นว่า .get(“/”) ภายในวงเล็บคือชื่อ path ที่เราจะระบุใช้งานอย่างเช่นในการทดสอบที่ Postman เช่น http://127.0.0.1:8000/ แต่ถ้าเปลี่ยนเป็น .get(“/hello”) ในการใช้ URL จะเป็นลักษณะนี้ http://127.0.0.1:8000/hello ในโค้ดคุณอาจจะเป็น async อยู่ก่อนการประกาศฟังก์ชัน นั่นก็เพื่อให้มันทำงานแบบ asynchronous นั่นเอง ก็คิดง่ายๆว่าเรา เข้าเว็บนึงแล้วจะดูข้อมูลอะไรสักอย่างถ้าเน็ตเราไม่ได้เราก็ต้องทำการรอจนกว่าข้อมูลจะมาครบเราถึงจะเอาข้อมูลในเว็บนั้นไปใช้งานได้ ก็คล้ายๆกับ async โดยในส่วนที่รอจะเรียกว่า await รอจนกว่าจะเสร็จค่อยไปทำอย่างอื่นต่อประมาณนั้น

คุณสามารถส่งข้อมูลเข้ามาได้ในลักษณะนี้ :

@app.get("/{message}")
async def hello_world(message):
return {"message": message}

เมื่อเทสใน Postman url คือ http://127.0.0.1:8000/hello wk:

หรือคุณอยากรับข้อมูลมากกว่า 1 ตัว:

@app.get("/{message}/{name}")
async def hello_world(message, name):
return {"message": f"{message} - {name}"}

ทดสอบใน Postman url คือ http://127.0.0.1:8000/hello/wk:

หรือคุณอยากจะเอาข้อมูลของ name จาก url http://127.0.0.1:8000/?name=wk นี้ก็ทำได้แบบนี้:

from fastapi import FastAPI, Request
from models import User

app = FastAPI()


@app.get("/")
async def hello_world(request: Request):
name = request.query_params.get("name")
return {"message": name}

ลองทดสอบเหมือนเดิมเลย

คุณก็จะได้ข้อความมาแล้วในลักษณะประมาณนั้น แต่การส่งข้อมูลแบบนี้อาจจะถูกดักข้อมูลได้โดยบางท่าเราอาจจำเป็นต้องส่ง post เพื่อสิ่งที่ดีกว่าในหัวข้อต่อไป

Method POST

เราจะทำการสร้าง method post กันโดยใช้ชื่อว่า /add-user และ ตั้งชื่อฟังก์ชันเป็นอะไรก็ได้ แต่พยายามตั้งให้มันเหมือนสมกับ path หน่อยเพื่อจะได้ไม่งงทีหลังแล้วกันนะ โดยเราจะทำการรับข้อมูลผ่าน request โดย request จะเป็น payload ที่เราส่งเข้ามาซึ่งในส่วนของ payload คืออะไรก็คือก้อน object หรือ json นั่นเองที่เราจะต้องส่งเข้ามา โดยลักษณะข้อมูลจะเป็นลักษณะนี้

{
"name":"test"
}

แล้วเราก็จะเอามาเก็บใส่ตัวแปร name แล้ว return ข้อมูลออกไปแบบนี้:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

@app.post("/add-user")
async def add_user(request: dict):
name = request.get('name')
return {"message": name}

เมื่อเราเอาไปเทสใน Postman ทำการเปลี่ยน URL เป็น http://127.0.0.1:8000/add-user และเปลี่ยน raw ด้วย ก็จะเป็นลักษณะนี้:

จากนั้น payload นี้ไปใส่ แล้วกด Send ได้เลย:

{
"name":"test"
}

การใช้ Pydantic

Pydantic เป็นไลบรารีที่ใช้สำหรับการประกาศโมเดลข้อมูล (data models) ใน FastAPI โดยมีวัตถุประสงค์เพื่อช่วยให้เราสามารถกำหนดรูปแบบของข้อมูลที่เราต้องการรับหรือส่งออกได้อย่างง่ายดาย นอกจากนี้ Pydantic ยังมีฟีเจอร์การตรวจสอบความถูกต้องของข้อมูลที่มีประสิทธิภาพ ทำให้เราสามารถตรวจสอบและจัดการข้อผิดพลาดที่เกิดขึ้นได้อย่างมีประสิทธิภาพ

เพื่อให้โค้ดเรานั้นดูดีและเมื่อนักพัฒนาคนอื่นมาดูโค้ดเราจะได้ง่ายเหมือนกันด้วยจำเป็นต้องใช้ Pydantic มากำหนดทิศทางหรือสิ่งที่ต้องการเข้ามาจาก payload ใน FastAPI เราสามารถประกาศโมเดลข้อมูลโดยใช้ Pydantic ได้โดยใช้เครื่องหมาย : เพื่อระบุชนิดข้อมูลของแต่ละฟิลด์ ตัวอย่างการประกาศโมเดลข้อมูลด้วย Pydantic แบบนี้:

from pydantic import BaseModel

class User(BaseModel):
id: int
username: str
email: str

เรามารวมกันโค้ดเราก็จะเป็น:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
id: int
username: str
email: str

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

@app.post("/add-user")
async def add_user(user: User):
username = user.username
return {"message": username}

ทดสอบใน Postman ด้วย payload:

{
"id":1,
"username":"test",
"email":"test@test.com"
}

หากกรณีไม่ครบเช่น:

{
"id":1,
"username":"test"
}

แต่เพื่อความ clean ก็ควรแยกส่วนออกไปเป็นอีกหนึ่งไฟล์จะดีกว่าเพราะงั้นสร้างไฟล์ใหม่ชื่อว่า models.py เพื่อเก็บ class User(BaseModel): ไว้ต่างหากแล้วค่อย import เข้ามาใช้งานงั้นเริ่มกัน

ในไฟล์ models.py:

from pydantic import BaseModel

class User(BaseModel):
id: int
username: str
email: str

ในไฟล์ main.py:

from fastapi import FastAPI
from models import User

app = FastAPI()

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

@app.post("/add-user")
async def add_user(user: User):
username = user.username
return {"message": username}

สวยงามมากกว่า และอนาคตหากจะสร้าง class pydantic ก็ไปสร้างที่ไฟล์ models.py แล้วค่อยเรียกออกมาใช้งานลักษณะนี้

Method Put

จะใช้ในการแก้ไขข้อมูลทั้งหมดหรือเป็นการเปลี่ยนค่าใหม่ทั้งหมดของข้อมูล ตัวอย่างเช่น

  • นาย ก ชอบกินลูกอม และชอบสีเหลือง

ใช้ put ในการเปลี่ยนข้อมูล

  • นาย ก ชอบขนม และชอบสีดำ

อะไรประมาณนั้น เพื่อให้เห็นภาพตอนที่เรา post เรายังไม่ได้เก็บข้อมูลอะไรไว้เลยเพราะงั้นจะไปแก้กันก่อนลุย

from fastapi import FastAPI
from models import User

app = FastAPI()

users_list = []

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

@app.get("/users")
async def users():
return users_list

@app.post("/add-user")
async def add_user(user: User):
users_list.append(user)
return {"message": "เพิ่มผู้ใช้งานเรียบร้อยแล้ว"}

โดยสร้างตัวแปร users_list ขึ้นมาเป็น global และเพิ่มเส้น /users เพื่อแสดงข้อมูลผู้ใช้งานทั้งหมด ในเส้น /add-user หากมีการส่ง payload เข้ามาครบก็จะถูกเพิ่มเข้าไปใน users_list

ต่อไปเราจะไปทำการเขียนเส้น method put กัน:

from fastapi import FastAPI
from models import User

app = FastAPI()

users_list = []

@app.get("/")
async def hello_world():
return {"message": "สวัสดี FastAPI!"}

@app.get("/users")
async def users():
return users_list

@app.post("/add-user")
async def add_user(user: User):
users_list.append(user)
return {"message": "เพิ่มผู้ใช้งานเรียบร้อยแล้ว"}

@app.put("/edit-user/{user_id}")
async def edit_user(user: User, user_id: int):
if len(users_list) > 0 :
for i in range(len(users_list)):
if users_list[i].id == user_id:
users_list[i] = user
else:
return {"message": "ไม่พบผู้ใช้งาน"}
return {"message": "อัพเดทผู้ใช้งานเรียบร้อยแล้ว"}
else:
return {"message": "ไม่พบผู้ใช้งาน"}

ในการทำงานของโค้ดคือจะทำการรับ id ผู้ใช้งานที่จะแก้ไขเข้ามาแล้วทำไการเช็คว่า len(users_list) > 0 เท่าเพื่อป้องกันกรณีไม่ได้มีผู้ใช้งาน จากนั้นทำการวนซ้ำเพื่อเปลี่ยนแปลงข้อมูลตาม payload ที่รับเข้ามา

นี่คือตัวอย่างการใช้ PUT ก็จะทรงๆนี้ครับ

Method PATCH

บางท่าเราก็ไม่ได้อยากแก้ไขข้อมูลมันทุกตัวหรอก เราจะแก้บางตัวเท่านั้น เพราะงั้นการมีอยู่ PATCH เลยเข้ามาช่วยตรงนี้จะจำง่ายๆก็เหมือนกับอัปเดตแพทช์อะไรประมาณนี้ เพราะอัปเดตแพทช์คืออัปเดตอะไรบางอย่างหรือเปลี่ยนแปลงบางอย่างไม่ใช่ว่าจะเปลี่ยนทุกระบบในเกมงั้นมาดูตัวอย่างโค้ดกัน

@app.patch("/update-user/{user_id}")
async def update_user(user: User, user_id: int):
if len(users_list) > 0 :
for i in range(len(users_list)):
if users_list[i].id == user_id:
users_list[i] = User(**user.dict())
else:
return {"message": "ไม่พบผู้ใช้งาน"}
return {"message": "อัพเดทผู้ใช้งานเรียบร้อยแล้ว"}
else:
return {"message": "ไม่พบผู้ใช้งาน"}

จากโค้ดรูปแบบคล้ายกับ put มาก แต่ที่ต่างคือ users_list[i] = User(**user.dict())

ทดสอบใน Postman กัน payload คือ

    {
"id":1,
"username":"admin_1234"
}

ผลลัพธ์จะบอกว่าต้องส่ง email มาด้วยนะ แต่ถ้าเราส่ง email ไปมันก็จะเป็น Put ไม่ใช่หรือ เพราะงั้นเราจะไปแก้ไขกันที่ไฟล์ models.py เราจะใส่ค่าเริ่มต้นกรณีไม่ใช่ payload เข้ามา ในลักษณะแบบนี้

from pydantic import BaseModel


class User(BaseModel):
id: int
username: str
email: str = ""

กลับไปแก้ main.py เพื่อดักข้อมูลกันต่อ

@app.patch("/update-user/{user_id}")
async def update_user(user: User, user_id: int):
if len(users_list) > 0:
for i in range(len(users_list)):
if users_list[i].id == user_id:
if user.email == "":
user.email = users_list[i].email
users_list[i] = User(**user.dict())
else:
return {"message": "ไม่พบผู้ใช้งาน"}
return {"message": "อัพเดทผู้ใช้งานเรียบร้อยแล้ว"}
else:
return {"message": "ไม่พบผู้ใช้งาน"}

โดยจะทำการดักถ้าไม่ได้ส่ง email ก็จะไปดึงข้อมูลเก่าที่อยู่ใน users_list ออกมา

    if user.email == "":
user.email = users_list[i].email

ทำการทดสอบกัน

แล้วดูข้อมูลกัน

Method DELETE

ส่วนตรงนี้จะง่ายมากทำคล้ายๆกับที่ผ่านๆมาเราต้องการแค่ user_id แล้วจะทำไปลบออกใน users_list และเราจะสร้างเส้น /delete-user/{user_id} ในลักษณะนี้ เป็นยังไงมาลุยกันเลย

@app.delete("/delete-user/{user_id}")
async def delete_user(user_id: int):
if len(users_list) > 0:
for i in users_list:
if i.id == user_id:
users_list.remove(i)
return {"message": "ลบผู้ใช้งานเรียบร้อยแล้ว"}
else:
return {"message": "ไม่พบผู้ใช้งาน"}

ทำการทดสอบกัน โดยเราจะไม่ส่ง payload ไปเพราะเราจะส่งคล้ายกับ get โดยข้อมูลที่มีจะมีสองก้อน

จากนั้นเราจะไปลบกันด้วยเส้นที่เราสร้างขึ้นมากัน

กลับไปดูข้อมูลทั้งหมดกัน

ต่อไปคือส่วนเสริม

  • สร้างเส้น get-user/{user_id}

ข้อมูลที่เราดึงมาตอนนี้คือเป็นการดึงข้อมูลทั้งหมดออกมา แต่เราอยกาดึงข้อมูลแค่ตัวหล่ะทำยังไง เราก็จะสร้างเส้น get-user/{user_id} แล้วส่งข้อมูลแค่ตัวเดียวออกไปกันลุย

@app.get("/get-user/{user_id}")
async def get_user(user_id: int):
if len(users_list) > 0:
for i in users_list:
if i.id == user_id:
return i
return {"message": "ไม่พบผู้ใช้งาน"}
else:
return {"message": "ไม่พบผู้ใช้งาน"}

มาทดสอบกัน

  • สร้าง autoid

จะดีกว่าไหมอยากให้ระบบสร้างเลข id เรียงให้เราอัตโนมัติไปเลย ตอนส่ง payload จะได้ไม่ต้องกังวลเรื่องของ id ซ้ำกันไป เพราะงั้นมาลุยกัน

แน่นอนว่าเราจะไม่ id ไปใน payload แล้ว เพราะงั้นเราจะไปแก้ในไฟล์ models.py ให้ id มีค่าเริ่มต้นกัน

from pydantic import BaseModel


class User(BaseModel):
id: int = 0
username: str
email: str = ""

และไปแก้ในไฟล์ main.py ที่เส้น add-user โดยเราจะทำแบบง่ายที่สุดคือนับจำนวนของ users_list ว่ามีกี่ตัวโดยใช้ len() เข้ามาช่วยเท่ากับว่าถ้าไม่มีสมาชิกอะไรในนั้นมันจะได้ 0 ออกมานั่นเองมาดูโค้ดกัน

@app.post("/add-user")
async def add_user(user: User):
user.id = len(users_list)
users_list.append(user)
return {"message": "เพิ่มผู้ใช้งานเรียบร้อยแล้ว"}

มาทดสอบกันเลยโดย payload คือ

{
"username":"test",
"email":"rtes@cx.cd"
}

แล้วลอง Send สักสองสามคลิกดู แล้วไปทดสอบเส้น /users อีกครั้งก็จะเห็นว่ามันมีเรียงให้อัตโนมัติแล้ว!!

และนี่ก็คือคร่าวๆของ FastAPI โดยบทความต่อไปเราจะลองทำการเชื่อมต่อกับ database อย่าง mongoDB หรือรับ JWT เสร็จลง cookie กันดู ซึ่งก่อนหน้ามีบทความเกี่ยวกับเชื่อมการ Authentication ด้วย firebase สามารถไปลองดูกันเพิ่มเติมได้นะที่ https://medium.com/@watchakorn-18k/%E0%B8%81%E0%B8%B2%E0%B8%A3-authentication-%E0%B9%83%E0%B8%99-fastapi-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-firebase-admin-sdk-94c4eb1040d4 หวังว่าจะมีประโยชน์กันนะครับแล้วเจอกันใหม่

Github : https://github.com/watchakorn-18k

Linkedin : https://www.linkedin.com/in/watchakorn/

· 2 min read

generate from Bing

MongoDB คืออะไร?

MongoDB เป็นฐานข้อมูล NoSQL ประเภท Document-oriented ซึ่งหมายความว่าข้อมูลจะถูกเก็บในรูปแบบของเอกสาร JSON

การใช้งาน MongoDB กับ Python

การใช้งาน MongoDB กับ Python นั้นง่ายมาก เพียงแค่ติดตั้งไลบรารี PyMongo

pip install pymongo

แนะนำให้ใช้ virtual environment ก่อนติดตั้ง

python -m venv venv

เปิดใช้งาน virtual environment

"venv/Scripts/activate"

เซิร์ฟเวอร์ MongoDB

มีสองแบบคือ:

— ติดตั้งเปิดใช้งานเองบนเครื่องด้วยโปรแกรม WinNMP สำหรับ windows

— ใช้ฟรีจาก Cloud ของ https://www.mongodb.com/

Database Manager MongoDB

มีสองที่แนะนำ:

dbgate

Studio 3T

การเชื่อมต่อ MongoDB

database.py
from pymongo import MongoClient

# เชื่อมต่อกับ MongoDB
client = MongoClient("localhost", 27017)

# เข้าถึงฐานข้อมูล
db = client["my_database"]

# เข้าถึงคอลเล็กชัน
collection = db["my_collection"]

เพิ่มข้อมูลลงใน MongoDB

app.py
from database import collection

payload = {"name": "wk", "location": "thailand"}
collection.insert_one(payload)

ดึงข้อมูลจาก MongoDB

app.py
from database import collection

# ค้นหาข้อมูล
result = collection.find_one({"name": "wk"})

# ดึงข้อมูลทั้งหมด
result_all = collection.find()

print(result)
print([x for x in result_all])

อัปเดตข้อมูลใน MongoDB

app.py
from database import collection

# อัปเดตข้อมูล
collection.update_one({"name": "wk"}, {"$set": {"name": "watchakorn"}})

ลบข้อมูลใน MongoDB

app.py
from database import collection

# ลบข้อมูล
collection.delete_one({"name": "watchakorn"})

เพิ่มข้อมูลหลายรายการใน MongoDB

app.py
from database import collection

# เพิ่มข้อมูลหลายรายการ
documents = [
{"name": "John Doe", "age": 30},
{"name": "Jane Doe", "age": 25},
{"name": "Peter Smith", "age": 40},
]
collection.insert_many(documents)

ค้นหาและอัปเดตข้อมูลในหนึ่งขั้นตอนใน MongoDB

app.py
from database import collection

# เพิ่มข้อมูลหลายรายการ
document = collection.find_one_and_update(
{"name": "Jane Doe"}, {"$set": {"age": 31}}
)

· 11 min read

generate from Bing


การ Authentication ใน FastAPI ด้วย Firebase Admin SDK

สวัสดีครับ เหล่านักพัฒนาผู้รักความสะดวก!

เบื่อไหมครับกับการเขียนโค้ด Authentication ซ้ำๆ วนไปวนมา ‍ อยากจะมีระบบที่ใช้งานง่าย ปลอดภัย และรวดเร็วทันใจ ⚡️ ถ้าใช่! บทความนี้เหมาะกับคุณมาก

วันนี้ผมจะมาแนะนำวิธีการใช้ Firebase Admin กับ FastAPI สองยักษ์ใหญ่ที่จะช่วยให้คุณสร้างระบบ Authentication สุดเจ๋งได้แบบง่ายๆ โดยไม่ต้องเสียเวลาเขียนโค้ดเยอะแยะ บทความนี้แทบจะละเอียดมาก ไม่เหมาะกับผู้ขี้เกลียดอ่านอะไรเยอะแยะ 😁 เพราะงั้น ขั้นตอนไหนคุณรู้อยู่แล้วข้ามไปได้เลย 🧐

เตรียมตัวให้พร้อม แล้วไปลุยกันเลย!

ขอบเขตเครื่องมือ

ToolVersionLink
Python3.11.5https://www.python.org/downloads/release/python-3115/
Visual Studiocurrenthttps://code.visualstudio.com/
Postmancurrenthttps://www.postman.com/
OSWindows 11

เริ่มต้นใช้งาน Firebase Admin

  1. เข้าใช้งาน Firebase ก่อน

  • ตั้งชื่อได้อิสระได้เลย เสร็จแล้วก็กด Continue โล้ดดดด

  • จะเป็นการถามว่าจะใช้งาน Google Analytics หรือเปล่า ถ้าไม่ก็ติ๊กตรง Enable… มันก็จะปิด แต่ว่าระบบแนะนำให้เปิด ก็เปิดแล้วกัน จากนั้นกด Continue ได้เลย แต่กรณีคุณปิดมันจะเป็นปุ่ม Create project

  • หากคุณเปิดเพราะว่าเชื่อบทความ (^_^) ทำการเลือกบัญชีได้เลย แต่หากไม่เคยใช้ Google Analytics อาจจะต้องสร้างบัญชีก่อนนะครับถึงจะมีให้เลือก แต่ถ้าไม่อยากวุ่นกดที่ Previous เพื่อย้อนกลับไปปิดก็ได้เช่นกัน เพื่อทุกอย่างเรียบร้อยกด Create project ได้เลย

  • จากนั้นก็รอสักระยะ

  • เสร็จแล้ว!!! กด Continue ได้เลย!

  • เมื่อมาถึงหน้านี้หากท่านยังไม่เคยใช้งานมาก่อน ตั้งสติและใจเย็นๆ 😮‍💨😮‍💨 บทความนี้เราจะใช้แค่ Authentication เท่านั้น! 🧐

  • แทบเมนูด้านซ้ายเลือก Build และ **Authentication **ที่เราหมายปอง

  • เช่นเดิมหน้านี้ก็ใจเย็นๆ จุดหมายเราคือปุ่ม Get stated แต่ถ้าคุณอยากรู้อะไรเพิ่มเติมก็ดูคลิปที่เขานำมาให้รอก็ได้ ถ้าไม่เก่งอังกฤษ (เหมือนผม 😅) ก็เปิดคำบรรยายแล้วแปลอัตโนมัติเป็นไทย ก็ได้เช่นกัน ถ้าพร้อมแล้วค่อยมากด Get stated

  • เหล่านี้คือเหล่า providers ที่เราสามารถใช้ในการ **Authentication **ได้แต่สิ่งที่เราสนใจแบบง่ายๆพื้นๆคือ **Email/Password **ส่วนอื่นๆไม่ต้องไปสนใจ! เพราะผมยังใช้ไม่เป็น 😎เพราะงั้นเลือก **Email/Password **ได้เลย

  • ทำการ Enable ที่ **Email/Password **แล้วเลือก Saveได้เลย ส่วน Email link (passwordless sign-in) ยังไม่ได้ลองเช่นกันครับผม เพราะงั้นช่างมัน 😅

  • ถ้าได้แบบนี้เป็นการเสร็จในส่วนนี้แล้ว

ต่อไปเราจะไปเตรียมโฟลเดอร์และไฟล์

จัดการโฟลเดอร์และไฟล์ให้เตรียมพร้อมใช้งาน

  1. สร้างโฟลเดอร์

  • เพื่อไว้เก็บทุกอย่างที่เราจะทำ ส่วนนี้จะเป็นส่วนที่ยากที่สุดเพราะไม่รู้ว่าจะใช้ชื่อว่าอะไรดี 😅 จริงๆจะใช้ชื่อว่าอะไรก็ได้ แต่ทางผมของตั้งชื่อว่า **fastapi-firebase-auth **แล้วกันจากนั้นเข้าไปในโฟลเดอร์ให้เรียบร้อย
  1. เราจะทำการนำโฟลเดอร์ที่เราสร้างไปใช้งานใน **Visual Studio Code **หากคุณใช้ windows คุณสามารถทำตามนี้ได้เลย (หากคุณใช้อย่างอื่นคุณต้องค่อยๆ cd ไปยังโฟลเดอร์ของคุณและใช้ code . ได้เหมือนกัน)

  1. เนื่องจากว่าคุณจำเป็นต้องติดตั้ง firebase_admin และ fastapi ดังนั้นเราจึงต้องสร้าง virtual environment ขึ้นด้วยด้วยคำสั่ง
python -m venv .env
  • โดย .env คือชื่อจะใช้ชื่ออะไรก็ แต่แนะนำว่าใช้ชื่อนี้ง่ายกว่า

  • เมื่อทุกอย่างเรียบร้อยจะเป็นแบบนี้จากนั้นทำการเปิดใช้งานด้วยคำสั่ง
.env/Scripts/Activate.ps1
  • หากคุณใช้ Terminal คือ Power Shell (ตรงนี้หากไม่ได้ลองศึกษาเรื่อง virtual environment python เพิ่มเติมนะครับ)

  • แต่หากไม่ได้จริงๆ ก็ไม่เป็นไร สำหรับมือใหม่มันอาจจะยุ่งยาก ทำขั้นต่อไปกันได้เลย!

ติดตั้งสิ่งต่างที่ต้องใช้กัน

  1. ติดตั้ง firebase-admin กัน https://pypi.org/project/firebase-admin/
pip install firebase-admin

  1. ติดตั้ง FastAPI กัน https://pypi.org/project/fastapi/ เพื่อใช้เป็น web framework
pip install fastapi

  1. ติดตั้ง Uvicorn กัน https://pypi.org/project/uvicorn/ เพื่อใช้เป็นตัว run server
pip install uvicorn

ส่วนนี้เสร็จเรียบร้อยต่อไปเราจะกลับไปที่ firebase เพื่อเอาอะไรบางอย่างจากมัน 🧐

ไปเอา Certificate จาก Firebase

  1. จากที่หน้าที่เราทำกันล่าสุดที่ firebase ทำการที่รูป ⚙️ หรือรูปเฟือง และเลือก Project settings

  1. จากนั้นไปที่แท็ป Service Accounts

  1. เลือกเป็น Python ก่อนจากนั้นก็ Generate new private key

  1. เลือก generate key

  1. คุณจะได้ไฟล์ .json มาจากนั้นนำมันมาใส่ไว้ในโฟลเดอร์ที่เราเตรียมก่อนหน้านี้

  1. เปลี่ยนชื่อใหม่ให้มันเพื่อความใช้งานง่าย จริงจะใช้ชื่อว่าอะไรก็ได้ แต่ส่วนนี้แนะนำแบบ 100% ให้ใช้ชื่อ **service-account.json **แบบนี้ดีกว่า ระวังด้วยหากคุณจะเปลี่ยนชื่อที่โฟลเดอร์เลย ไม่ต้องใช้ .json แต่ถ้าคุณเปลี่ยนใน vscode ต้องใช้ .json ด้วย ไม่งั้นจะเป็นลักษณะนี้

แบบที่ถูกคือ ✅

ข้อมูลในนี้สำคัญหากคุณจะนำไปใช้ต่อระวังอย่าให้หลุด แต่ถ้าหลุดก็ไปลบแล้วเริ่มใหม่ 😅

เรียบร้อย เราจะไปส่วนต่อไปกัน

ลุยโค้ดกันได้เลย !

  1. สร้างไฟล์ app.py ขึ้นมา

  2. จากนั้นเราจะมาเริ่มใช้งาน FastAPI กันโดยคัดลอกโค้ดด้านล่างไปใส่ที่ app.py ได้เลย

app.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}
  1. จากนั้น run server ด้วย uvicorn แล้วลอง test ด้วย Postman ดู
uvicorn app:app --reload
  • app แรกคือชื่อไฟล์ app.py และ app ต่อไปคือชื่อตัว app = FastAPI() และ reload คือ เมื่อคุณแก้ไขโค้ดและบันทึกมันจะรีโหลด web server ให้อัตโนมัติโดยไม่ต้องมาหยุดแล้วรันใหม่ โอเคนะ 😎

  1. สร้าง **init_firebase.py **เพื่อจะใช้เป็นเชื่อมต่อกับ firebase ด้วยไฟล์ service-account.json ที่เราได้มา โค้ดดังนี้
init_firebase.py
from firebase_admin import credentials, initialize_app

cred = credentials.Certificate("service-account.json")
initialize_app(cred)
print("firebase init done")
  1. กลับไปแก้ไขโค้ด app.py เพิ่มเติม
app.py
from init_firebase import *
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}
  1. แล้วบันทึกรอรีโหลดหากมีข้อความ **firebase init done **ก็ถือเรียบร้อย 😎

  1. สร้างไฟล์ชื่อว่า models.pyเราจะทำการกำหนดรูปแบบ payload ที่เราต้องการนำเข้ามาซึ่งเราจะทำแบบง่ายๆจะรับเข้ามาแค่ email กับ password แบบนี้
models.py
from pydantic import BaseModel


class User(BaseModel):
email: str
password: str
  1. เราได้รูปแบบ payload แล้วเราจะกลับไปที่ app.py เพื่อทำการสร้างเส้น api ใหม่คือเส้น /signup เพื่อใช้ในการสมัครสมาชิกนั้นเองโดยจะใช้ method เพื่อจะรับ payload เข้ามา และเหมือนเดิมคัดลอกไปวางทับโค้ดเดิมใน app.py ได้เลยอย่าลืมบันทึุกด้วย 😎
app.py
from init_firebase import *
from fastapi import FastAPI
from models import User

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}

@app.post("/signup")
def sign_up(user: User):
return user
  1. เทสใน Postman ได้เลยโดย payload เอาไปใส่ที่ตรง raw ได้เลยแล้วกด **Send **จะได้แบบรูปแบบ
payload
    {
"email":"wk23@gmail.com",
"password":"213sad2w2"
}

  1. ต่อเราจะทำการโค้ดในส่วนของการสมัครสมาชิกลงใน firebase กันจากโค้ดนี้ไปใส่ที่ app.py แล้วลองทดสอบเหมือนก่อนหน้าอีกครั้ง
app.py
from init_firebase import *
from fastapi import FastAPI
from models import User
from firebase_admin import auth

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}


@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
print(user)
return user

11 . ลองกลับไปที่ firebase ส่วนของ Authentication คุณจะเห็นว่ามี email ตัวอย่างของเราโผล่ที่นั่นแล้ว!!

  1. แต่หากกลับไป Postman เราได้ข้อมูลอะไรมาเยอะแยะเลย เราจะมาดูส่วนที่สำคัญหน่อยแล้วกัน

payload
    {
"_data": {
"localId": "l6fNK2I2RwOft7FXV1ACpEEgX3e2",
"email": "wk23@gmail.com",
"passwordHash": "UkVEQUNURUQ=",
"emailVerified": false,
"passwordUpdatedAt": 1708104981299,
"providerUserInfo": [
{
"providerId": "password",
"federatedId": "wk23@gmail.com",
"email": "wk23@gmail.com",
"rawId": "wk23@gmail.com"
}
],
"validSince": "1708104981",
"disabled": false,
"createdAt": "1708104981299"
}
}
  • localId หรือ user_id เป็นรหัสสุ่มขึ้นมาไม่ซ้ำหรือใช้มันเป็น primary key ได้นั่นเอง

  • emailVerified เป็นสถานะที่บอกว่าอีเมลที่สมัครสมาชิกเข้ามานี้ทำการยืนยันอีเมลแล้วหรือยัง

  1. ต่อเราจะทำการสร้างเส้น /signin กันหรือเส้นที่จะใช้ในการเข้าสู่ระบบกัน แต่ว่าโชคร้ายที่ firebase_admin นี้ไม่มีการ sign in ด้วย email และ password 😭 แต่อย่าเศร้ามีวิธีแก้ 😎
  • ทำการติดตั้ง requests https://pypi.org/project/requests/ เพิ่มเติม อย่าลืมหยุด web server โดยไปที่ Termibal การกด Ctrl + C
pip install requests
pip install python-dotenv
  • กลับไปที่ firebase เลือก Project settings

  • คัดลอกข้อมูลที่อยู่หลัง Web API Key ไว้

  • สร้างไฟล์ _env เฉยๆ ไม่มีนามสกุลไฟล์นะ ไว้เก็บข้อมูลสำคัญอย่าง Web API Key จากนั้นวางลงแบบนี้ อย่าลืมเปลี่ยน key นะของใครของมันถ้าไม่เชื่อ error ไม่รู้น๊า
_env
FIREBASE_WEB_API_KEY=AIzaSyBQ8J_XYglnULI1U8vHnPYD-uz1TEjrxZY
  • สร้างไฟล์หน่อยแล้วกันเวลาเรียก FIREBASE_WEB_API_KEY จะได้เรียกง่ายๆชื่อว่า settings.py แล้วใส่โค้ดนี้ลงไปได้เลย
settings.py
import dotenv
import os

dotenv.load_dotenv("_env")

FIREBASE_WEB_API_KEY = os.environ.get("FIREBASE_WEB_API_KEY")
  • แล้วทำการสร้างไฟล์ rest_firebase_api.py เราจะทำการใช้ของที่ firebase_admin ของ python ที่ไม่ได้ใส่มาใช้กัน คัดลอกโค้ดไปได้เลย
rest_firebase_api.py
from settings import FIREBASE_WEB_API_KEY
import requests
import json

SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)


def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)

r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)

return r.json()
  • ต่อกันยังไม่จบในส่วนนี้เราได้ทำการสร้างฟังก์ที่ไว้ใช้ sign in เรียบร้อยแล้ว ต่อเราจะไปเขียนโค้ดในส่วน app.py กันต่อเหมือนเช่นเดิม คัดลอกไปเลย
app.py
from init_firebase import *
from fastapi import FastAPI
from models import User
from firebase_admin import auth
from rest_firebase_api import sign_in_with_email_and_password

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}


@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user


@app.post("/signin")
def sign_in(user: User):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
return user
  1. บันทึกแล้วรันด้วยคำสั่งเดิมและเทสใน Postman ได้เลยถ้าขึ้นแบบข้างล่างแสดงว่าสำเร็จ หากไม่สำเร็จเช็ค email กับ password ใน payload ใหม่ดูน๊า

  1. ทีนี้ลองเทสกรณีรหัสผิดหรืออีเมลผิดดู
payload
    {
"email":"pg23@gmail.com",
"password":"213sad2w2"
}

กับ

payload
    {
"email":"wk23@gmail.com",
"password":"sdsddsd"
}

payload
    {
"email":"wk23@gmail.com",
"password":"213sad2w2"
}

  1. ถือว่าเยี่ยมเลยเราทำระบบ login เสร็จเรียบร้อยแล้วต่อไปเราจะให้มัน response ข้อมูล email_verified เข้ามาด้วยเพราะเราอยากรู้สถานะของผู้ใช้รายนี้ว่ายืนยันอีเมลหรือยังเราอาจจะเอาสถานะนี้ไปจำกัดสิทธิ์อะไรในอนาคตได้ เริ่มกันเหมือนเดิมคัดลอกไปได้เลย เราจะแก้ไขโค้ดในส่วน *def sign_in(user: User): *นี้เท่านั้นเพราะงั้นจะเอามาแค่ส่วนนี้จะได้ประหยัดพื้นที่นะ ที่ไฟล์ **app.py **เหมือนเช่นเคย
app.py
@app.post("/signin")
def sign_in(user: User):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
return user
  1. ลองเทสบน Postman ได้เลย ก็จะพบมาสถานะยังไม่ได้ยืนยันอีเมล

  1. และเส้นต่อไปแน่น่อนว่าต้องเป็นเส้น /send-email-verify โดยใช้เป็น method get หากมายิงเส้นส่งอีเมลไปหาอีเมลของผู้ใช้ที่มาการสมัครสมาชิกเข้ามา แต่ว่าการจะใช้เส้นนี้ได้จำเป็นต้องมี idToken สิ่งจะมาจาก เส้น /signin

โดยจะมี 2 ท่าให้เล่นคือ

    1. เส้น /send-email-verify ทำการรับ email และ password เข้ามาและเรียกฟังก์ชัน sign_in_with_email_and_password(email, password) เหมือน เส้น /signin วิธีนี้ไม่จำเป็นต้อง sign-in ก็ verify email ได้
    1. เส้น /signin หลังจากยิงเส้นนี้แล้วให้ทำการบันทึก idToken เป็น cookie เอาไว้ แล้วเส้น /send-email-verify ไม่จำเป็นต้องรับอะไรเข้าไป ไปดึง cookie idToken ที่บันทึกมาใช้ได้เลย วิธีนี้เลยจำเป็นต้อง sign-in ก่อนถึงจะ verify ได้

แน่นอนว่าเราเป็นคนคูลๆแบบสองมันดูยากกว่าแบบแรก เพราะงั้นเราเลือกวิธีที่ 2 😅 เพราะมันดูสมเหตุสมผลกว่าแค่นั้นเอง งั้นมาเริ่มโค้ดกันเหมือนเดิมเราจะไปแก้ที่ เส้น /signin โค้ดด้านล่างก็เอาไปใส่ที่ app.py

app.py
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response
from models import User
from firebase_admin import auth
from rest_firebase_api import sign_in_with_email_and_password

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}


@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user


@app.post("/signin")
def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user

โดยจะดักด้วยว่าถ้าผู้ใช้งานกรอกอีเมลหรือรหัสผ่านไม่ถูกจะไม่ทำการ set_cookie

— key จะเปลี่ยนเป็นชื่ออื่นได้ — value คือข้อมูล idToken ที่อยู่ใน user — expires=datetime.utcnow() + datetime.timedelta(hours=1) cookie นี้จะถูกลบหรือหมดอายุไปในอีก 1 ชั่วโมง สามารถเปลี่ยนได้เช่น days=1 หรือ minutes=1 หรือ seconds=10 และแปลงเป็น timestamp อีกที

  • บันทึกแล้วทดสอบได้เลย แล้วไปที่แท็ป Cookies

  • ก่อนที่เราจะไปสร้างเส้น /send-email-verify กันต่อคงต้องหยุดก่อนสหาย เพราะว่าเสียใจด้วย firebase_admin ก็ไม่มี send-email-verify ให้เราใช้อีกแล้วครับท่าน 😭 ตั้งนั้นเราต้องใช้ท่าวรยุทธ์ rest เหมือนการ sign in เลย เพราะงั้นกลับไปที่ rest_firebase_api.py แล้วใส่โค้ดนี้ลงไปได้เลย
rest_firebase_api.py
from settings import FIREBASE_WEB_API_KEY
import requests
import json

SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)

SEND_VERIFY_EMAIL_URL = "https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode"


def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)
r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)

return r.json()


def send_verify_email(id_token):
headers = {"Content-Type": "application/json"}
url = f"{SEND_VERIFY_EMAIL_URL}?key={FIREBASE_WEB_API_KEY}"
data = {"requestType": "VERIFY_EMAIL", "idToken": id_token}
response = requests.post(
url, headers=headers, json=data, auth=("api_key", FIREBASE_WEB_API_KEY)
)
return response
  • เรียบร้อยพร้อมใช้งานที่นี้ไปสร้างเส้น /send-email-verify ที่ **app.py **ได้แล้ว 😎 ลุย
app.py
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response, Request
from models import User
from firebase_admin import auth
from rest_firebase_api import send_verify_email, sign_in_with_email_and_password

app = FastAPI()


@app.get("/")
def index():
return {"message": "Hello World"}


@app.post("/signup")
def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user


@app.post("/signin")
def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user


@app.get("/send-email-verify")
def send_email_verify(request: Request):
id_token = request.cookies.get("id_token")
if id_token:
send_verify_email(id_token)
else:
return {"message": "please login first 😭"}
return {"message": "send email verify 😎"}
  • มาลองเทสกับ อันดับแรกอาจจะต้องใช้ อีเมลที่เราสามารถรับส่งที่ firebase จะส่งมาให้เราได้ หรืออาจจะใช้ Temp Mail ทดสอบก็ได้

  • ขั้นแรกต้องเริ่มตั้งแต่ต้น คือ signup นั่นเอง เริ่มกัน ผมจะใช้ Temp Mail อันดับแรกไปเอามาก่อนที่ Temp Mail คัดลอกอีเมลมาโดยอีเมลมันจะสุ่มนะบอกไว้ก่อนอย่าใช้ตามผมเชื่อผม 😎 ใช้ของที่คุณได้ดีกว่า

  • และเตรียม payload ไว้
payload
    {
"email":"cahinab325@fkcod.com",
"password":"12345678"
}
  • เอาไปใส่แล้วยิงเส้น /signup ได้เลย

  • และเราต้อง sign in ก่อนถึงจะใช้ เส้น /send-email-verify ได้เพราะไม่งั้น อาจจะเกิดอาการเศร้าได้นะ 😭 เช่นแบบนี้ เพราะผมดักไว้แล้ว 😁

  • โอเคเอาจริงล่ะ เราต้อง sign in ก่อน เริ่มได้ payload เดิมเลย ยิงที่เส้น /signin กันลุย
payload
    {
"email":"cahinab325@fkcod.com",
"password":"12345678"
}

  • แล้วต่อพระเอกของเราคือเส้น /send-email-verify 🥳 มาลองดูกัน อย่าลืมเปลี่ยน method เป็น GET นะ

  • ต่อไปก็เช็คที่ Temp Email ที่เราได้มาได้เลยว่ามีอะไรส่งมาไหม?

  • ทำการยืนยันอีเมลโดยคลิกลิงก์ได้เลย

  • ก็จะได้หน้าตาแบบนี้นั่นเอง

  • กลับตรวจสอบที่เส้น /signin อีกรอบว่า email_verified เปลี่ยน true หรือยัง ถ้าเรียบร้อยแสดงว่าใช้งานได้

  1. แน่นอนว่าข้อความที่ส่งไปในอีเมลของผู้ใช้นั้นดูไม่สวยเลย คุณสามารถแต่งได้นะโดยไปที่ firebase ที่ Authentication แล้วเลือกแท็บ Templates

  1. แก้ไขข้อความบางส่วนได้ไม่สามารถแก้ Message ได้เพราะ firebase บอกว่าเพื่อเป็นการป้องกันสแปม

  1. แต่คุณสามารถเปลี่ยนภาษาได้นะ โดยไปที่ Template language

คร่าวๆ ก็จะประมาณนี้ แต่ถ้าอยากไปต่อหล่ะก็ลิงก์ที่มันยาวๆ ตอนยืนยันอีเมล์ เราสามารถจัดการให้มันยิงตรงมายัง web server เราได้ 😎 เป็นยังไปดูส่วนถัดไปเลย

ปรับแต่ง URL ยืนยันอีเมล

  1. เราจะต้องทำการเขียนโค้ดกันต่อโดยที่ **rest_firebase_api.py **เราจะทำการเปลี่ยน url จาก https://fir-fastapi-33292.firebaseapp.com เป็นเส้น api ที่สร้างขึ้นเอง แต่ก่อนจะไปจุดนั้นต้องสร้างฟังก์ชันที่จะใช้ในการรองรับมันก่อนโดยการทำงานเดิมนั้น firebase จะส่งออกมาสองข้อมูล คือ mode=action&oobCode=code หรือก็คือ mode=verifyEmail&oobCode=IpA6RRNFU….. บลาๆ แต่สิ่งที่เราสนใจคือ oobCode เพราะงั้นเราจะทำการดึงข้อมูล oobCode ออกมาจาก url บ้านั่นกันและเอามาใช้กันในฟังก์ชันที่จะสร้างขึ้นชื่อว่า verify_email แน่นอนเตรียมลุยมาดูกันจะออกมาท่าวรยุทธ์ไหน
rest_firebase_api.py
from settings import FIREBASE_WEB_API_KEY
import requests
import json

SIGN_IN_WITH_PASSWORD_URL = (
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
)

SEND_VERIFY_EMAIL_URL = "https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode"

VERIFY_EMAIL_URL = (
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/setAccountInfo"
)


def sign_in_with_email_and_password(
email: str, password: str, return_secure_token: bool = True
):
payload = json.dumps(
{"email": email, "password": password, "returnSecureToken": return_secure_token}
)
r = requests.post(
SIGN_IN_WITH_PASSWORD_URL, params={"key": FIREBASE_WEB_API_KEY}, data=payload
)

return r.json()


def send_verify_email(id_token):
headers = {"Content-Type": "application/json"}
url = f"{SEND_VERIFY_EMAIL_URL}?key={FIREBASE_WEB_API_KEY}"
data = {"requestType": "VERIFY_EMAIL", "idToken": id_token}
response = requests.post(
url, headers=headers, json=data, auth=("api_key", FIREBASE_WEB_API_KEY)
)
return response


@app.get("/verify-email")
async def verify_email_link(request: Request):
oob_code = request.query_params.get("oobCode")
print(oob_code)
if oob_code:
response = verify_email(oob_code).json()
if "error" in response:
return {"message": response["error"]["message"]}
else:
return {"message": "please click link at email 😭"}
return {"message": "verify email 😎"}
  1. มาสร้างเส้นที่จะให้ผู้ใช้งานเข้ามายืนยันกัน ขอใช้ชื่อว่า /verify-email แล้วกันง่ายดี มาลุยโค้ด จะมีการปรับขนาดใหญ่ เพราะผมลืม async 😅 จริงๆควรใส่ทุก ฟังก์ชันเลย เพราะว่าลืมแต่ครั้งนี้ใส่ให้ครบแล้ว กลับที่ **app.py **แล้วลุยเลย
app.py
from datetime import datetime, timedelta
from init_firebase import *
from fastapi import FastAPI, Response, Request
from starlette.requests import Request as StarletteRequest
from models import User
from firebase_admin import auth
from rest_firebase_api import (
send_verify_email,
sign_in_with_email_and_password,
verify_email,
)

app = FastAPI()


@app.get("/")
async def index():
return {"message": "Hello World"}


@app.post("/signup")
async def sign_up(user: User):
email = user.email
password = user.password
user = auth.create_user(email=email, password=password)
return user


@app.post("/signin")
async def sign_in(user: User, response: Response):
email = user.email
password = user.password
user = sign_in_with_email_and_password(email, password)
email_verified = auth.get_user_by_email(email).email_verified
user["email_verified"] = email_verified
if "idToken" in user:
response.set_cookie(
key="id_token",
value=user["idToken"],
expires=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
)
return user


@app.get("/send-email-verify")
async def send_email_verify(request: Request):
id_token = request.cookies.get("id_token")
if id_token:
send_verify_email(id_token)
else:
return {"message": "please login first 😭"}
return {"message": "send email verify 😎"}


@app.get("/verify-email")
async def verify_email_link(request: Request):
oob_code = request.query_params.get("oobCode")
print(oob_code)
if oob_code:
response = verify_email(oob_code).json()
if "error" in response:
return {"message": response["error"]["message"]}
else:
return {"message": "please click link at email 😭"}
return {"message": "verify email 😎"}
  1. เนื่องจากว่าเราเคยสมัครบัญชีนี้แล้ว ถ้าไม่อยากเปลืองอีเมลก็สามารถไปลบได้ที่ firebase เลยเหมือนเดิมแล้วก็ไปที่ Users แล้วเลือกผู้ใช้ที่จะลบ ถ้ายังไม่ขึ้นลองกดปุ่ม reset ข้างปุ่ม Add user ทางขวา

  1. ทำการเปลี่ยน url ที่จะถูกส่งไปยังอีเมลก่อน ไปที่ Templates แล้วเลือกรูปดินสอหรือแก้ไข

  1. เลือก Customize action URL แล้วก็ใส่ http://localhost:8000/verify-email หรือ http://127.0.0.1:8000/verify-email แล้วก็ Save เลย

  1. แล้วก็เทสเหมือนเดิมตั้งแต่แรกเลย ขอส่งเป็นรูปยาวๆนะจ๊ะ 😁
payload
    {
"email":"toxis52325@fkcod.com",
"password":"12345678"
}

เส้น /signup

เส้น /signin

เส้น /send-email-verify

หน้าอีเมล

หลังคลิกลิงก์

เส้น /signin ก่อน verify email

เส้น /signin หลัง verify email

ยอดเยี่ยมไปเลยใช่มั้ยการ Authentication ด้วย Firebase จริงๆมีอะไรที่อยากทำอีกเช่น Reset password , เปลี่ยนอีเมล , SMS verification และเชื่อมต่อฐานข้อมูลอย่าง MongoDB พวกนี้ แต่บทความนี้ยาวล่ะ ไม่รู้ว่าจะมีคนมาอ่านหรือเปล่า 😅 แต่ก็นั่นแหละใครที่มาอ่านถือว่าเรามีวาสนาต่อกันนะครับ ท้ายนี้ก็ขอบคุณที่อ่านบทความยันจบนะครับ เพราะว่าตอนแรกการใช้ Firebase สำหรับผมเป็นอะไรที่คิดว่ายากมากๆเลย แต่พอได้เริ่มเขียนและลองศึกษาอย่างจริงจังล่ะ เขาเตรียทุกอย่างไว้ให้เยอะมากนะครับ ใครอยากดูท่าวรยุทธ์ต่างๆเพิ่มเติมไปดูได้ที่ https://github.com/firebase/firebase-admin-python/blob/master/integration/test_auth.py เขาเขียนไว้ให้แล้วเราแค่ดึงมาใช้ หวังว่าจะได้เจอกันอีกบทความหน้านะครับถ้าผม ขยัน 😁 ใครติดโค้ดตรงไหนรันไม่ได้ไปดูได้ที่นี่ครับ https://github.com/watchakorn-18k/fastapi-firebase-auth