จาก Supabase ผ่าน Clerk สู่ Better Auth
(blog.val.town)- Val Town เลิกใช้ Supabase ในปี 2023 โดยย้ายฐานข้อมูลไปที่ Render และการยืนยันตัวตนไปที่ Clerk แต่พบว่าโครงสร้างที่ทำให้ความรับผิดชอบเรื่องผู้ใช้และเซสชันไปอยู่นอกระบบไม่เหมาะกับตน จึงย้ายไปใช้ Better Auth เมื่อหนึ่งเดือนก่อน
- Clerk เสนอแนวทางให้เลิกใช้ตารางผู้ใช้ แต่ Val Town ต้องแสดงคอนเทนต์ ชื่อผู้ใช้ และอวาตาร์ของผู้ใช้หลายคนบ่อย ๆ เพราะมีฟีเจอร์เชิงโซเชียล ทำให้เกิดความซับซ้อนเหมือนต้องดูแล ตารางผู้ใช้สองชุด จากข้อจำกัดของ Clerk API และการซิงก์ข้อมูล
- เมื่อ Clerk รับหน้าที่รีเฟรชเซสชันด้วย จึงกลายเป็น จุดล้มเหลวเพียงจุดเดียว และเมื่อ Clerk ล่ม ไม่ใช่แค่การล็อกอิน/ล็อกเอาต์ที่พัง แต่แม้แต่ผู้ใช้ที่ล็อกอินอยู่แล้วก็ใช้งานทั้งเว็บไซต์ได้ยาก โดยนับตั้งแต่เดือนพฤษภาคม 2025 หน้าสถานะบริการบ่งชี้ว่า uptime อยู่ระหว่าง 99% ถึง 99.9%
- Val Town ไม่รีไรต์ทันทีเพราะ SDK, ฟังก์ชันจัดการ, ระบบป้องกันการใช้งานในทางที่ผิด และแดชบอร์ดของ Clerk ยังมีประโยชน์ แต่ได้ตั้งหลักไว้ชัดเจนว่าจะไม่กลับไปเชื่อใจ การจัดการเซสชัน ของผู้ให้บริการภายนอกอีก
- Better Auth ตอบโจทย์ด้านคุณภาพโค้ด การผสานกับเฟรมเวิร์ก และมีแกนหลักเป็นโอเพนซอร์สอิสระ โดย Val Town รองรับทั้ง Clerk และ Better Auth พร้อมกันราว 2 สัปดาห์ รับคุกกี้สองแบบ และค่อย ๆ ทำ การย้ายเซสชัน แบบทีละขั้น
เบื้องหลังการย้าย
- Val Town เลิกใช้ Supabase ในปี 2023 แล้วเปลี่ยนไปใช้สถาปัตยกรรมฐานข้อมูลแบบดั้งเดิมมากขึ้น โดยใช้ Render สำหรับฐานข้อมูล และ Clerk สำหรับการยืนยันตัวตน
- ช่วงปลายปี 2023 มีการเปิด issue ว่าควรเลิกใช้ Clerk และ issue ดังกล่าวก็ถูกปิดเมื่อหนึ่งเดือนก่อนหลังจากย้ายไปใช้ Better Auth
- Clerk และ Supabase ต่างเป็นบริการที่ประสบความสำเร็จ โดยระดมทุนได้ 50 ล้านดอลลาร์ และ 100 ล้านดอลลาร์ที่มูลค่ากิจการ 5 พันล้านดอลลาร์ ตามลำดับ แต่โครงสร้างของ Val Town ขัดกับสิ่งที่ Clerk คาดหวังอย่างมาก
- ระหว่างการย้ายมีทั้งวิธีอ้อม บั๊ก และปัญหาขัดข้องจำนวนมาก โดยเฉพาะโครงสร้างที่ Clerk พยายามเข้ามาแทนทั้งตารางผู้ใช้และตารางเซสชันซึ่งกลายเป็นปัญหาหลัก
ปัญหาหลัก: การย้ายความรับผิดชอบของตารางผู้ใช้และตารางเซสชันออกไปภายนอก
-
ทำไม Clerk ถึงไม่เหมาะกับการใช้เป็นตารางผู้ใช้
- Clerk ผลักดันแนวคิดให้เลิกใช้ตารางผู้ใช้อย่างหนัก ดังจะเห็นจากบทความปี 2021 “Consider dropping your users table” และวิดีโอปี 2023 “DELETE your Users table”
- ตอนย้ายระบบในช่วงแรก Val Town มองว่าสามารถดึงข้อมูลอย่างการตั้งค่าผู้ใช้, URL อวาตาร์, อีเมล จาก Clerk API ได้เมื่อต้องการ
- ใน Clerk SDK,
rootAuthLoaderมีตัวเลือกloadUserสำหรับดึงข้อมูลผู้ใช้พร้อมจัดการการยืนยันตัวตนทั้งแอป ซึ่งทำงานได้ดีในสภาพแวดล้อมพัฒนา - แต่ในโปรดักชัน endpoint นี้ถูกจำกัดที่ 5 คำขอต่อวินาที ต่อทั้งบัญชีรวมทุกผู้ใช้ และปัญหานี้ถูกแก้ในภายหลังด้วยการเอาตัวเลือกนี้ออก
- บริการที่มีฟีเจอร์เชิงโซเชียลแบบ Val Town ต้องแสดงคอนเทนต์ ชื่อผู้ใช้ และอวาตาร์ของคนอื่นในหลายหน้า จึงไม่สอดคล้องกับสมมติฐานของ UI พื้นฐานของ Clerk ที่คิดว่าผู้ใช้จะอ่านแค่อวาตาร์และการตั้งค่าของตัวเองจาก JWT
- คำแนะนำจากฝั่ง Clerk คือให้ซิงก์อวาตาร์และข้อมูลอื่น ๆ ระหว่าง Clerk กับตารางผู้ใช้ของ Val Town สุดท้ายจึงกลายเป็นความซับซ้อนแบบต้องดูแล ตารางผู้ใช้สองชุด โดยมีแหล่งอ้างอิงข้อมูลเดียวกันอยู่สองที่
-
ความซับซ้อนของขั้นตอนสมัครที่เกิดจากการซิงก์ผ่าน webhook
- การซิงก์ข้อมูล Clerk เข้าฐานข้อมูลของ Val Town ต้องใช้ webhook ทำให้ขั้นตอนสมัครสมาชิกซับซ้อนและเปราะบาง
- ชั่วครู่หลังสมัคร ผู้ใช้อาจมีบัญชี Clerk แล้ว แต่ยังไม่มีแถวข้อมูลในฐานข้อมูลของ Val Town
- เพราะ Val Town บังคับให้มีชื่อผู้ใช้ จึงอาจเกิดสถานะที่ผู้ใช้มีทั้งบัญชี Clerk และแถวข้อมูลในฐานข้อมูลแล้ว แต่บัญชียังสร้างไม่สมบูรณ์
- การตั้งค่าผู้ใช้จึงต้องแยกเป็นส่วนที่ Clerk ดูแล เช่น วิธีการยืนยันตัวตน และส่วนที่ Val Town ต้องใช้เอง เช่น ชื่อผู้ใช้และการตั้งค่าเอดิเตอร์
โครงสร้างเซสชันที่ทำให้ Clerk กลายเป็นจุดล้มเหลวเพียงจุดเดียว
- เซสชันผู้ใช้แบบอิงคุกกี้มักมีอายุสั้นและต้องต่ออายุอยู่เรื่อย ๆ เพื่อให้สามารถเพิกถอนได้รวดเร็ว
- ในโครงสร้างนี้ ผู้ใช้ต้องแลกคุกกี้เซสชันเป็นคุกกี้ใหม่ทุกไม่กี่นาที โดยให้ซับโดเมนของ Val Town ส่งคำขอไปยัง Clerk เพื่อจัดการการต่ออายุ
- Val Town ไม่มีตารางเซสชัน และไม่ได้รับผิดชอบต่อเซสชันเอง
- วิธีนี้เหมาะหากต้องการหลีกเลี่ยงภาระเรื่องการยืนยันตัวตน แต่เมื่อ Clerk ล่ม ไม่ใช่แค่การล็อกอินและล็อกเอาต์ที่เสียหาย ทว่าผู้ใช้ที่ล็อกอินอยู่แล้วก็ไม่สามารถใช้งานทั้งเว็บไซต์ได้ตามปกติ
- Clerk มีปัญหาขัดข้องค่อนข้างบ่อยและกินเวลานาน และจากหน้าสถานะบริการหลังเดือนพฤษภาคม 2025 Uptime ที่รับรู้ได้อยู่ระหว่าง 99% ถึง 99.9%
- ความน่าเชื่อถือของระบบที่ซับซ้อนจะถูกจำกัดโดย ค่าต่ำสุด ของความน่าเชื่อถือจากองค์ประกอบสำคัญที่ประกอบกันขึ้นมา
- นอกจากสองปัญหาใหญ่นี้ ยังมีบั๊กและปัญหาอื่นที่เกี่ยวกับ Clerk อีกจำนวนหนึ่ง ซึ่งส่วนใหญ่สุดท้ายได้รับการแก้ไข แต่ต้องต่อสู้กับ “Stale Issue Bot” ที่ปิด issue อัตโนมัติด้วย
เหตุผลที่ยังไม่ย้ายทันที
- งานประเภท “ย้ายจาก X ไป Y” อาจกระทบทั้งความเร็วในการพัฒนาและสุขภาพจิตของทีม ดังนั้น Val Town จึงไม่อยากรีไรต์หากยังไม่จำเป็นจริง ๆ
- Clerk มี SDK สำหรับเทคโนโลยีที่ Val Town ใช้อยู่ เช่น Remix, Fastify และ Express และก็ตามการเปลี่ยนแปลงของเฟรมเวิร์กเหล่านี้ได้ค่อนข้างดี
- ฟังก์ชันด้านการจัดการและการป้องกันการใช้งานในทางที่ผิดของ Clerk ช่วยแก้ปัญหาฝั่งซัพพอร์ตลูกค้าและป้องกันผู้ใช้ที่มีพฤติกรรมฉ้อโกงได้
- พื้นที่ที่ Clerk เหมาะเป็นพิเศษคือแอปฝั่งฟรอนต์เอนด์ที่ค่อนข้างเรียบง่าย ไม่มีองค์ประกอบเชิงโซเชียล และไม่จำเป็นต้องมีตารางผู้ใช้
- เริ่มต้นใช้งานได้ง่าย ค่าใช้จ่ายก็รับได้ และแดชบอร์ดของ Clerk ก็ค่อนข้างดี
- ทางเลือกด้านการยืนยันตัวตนมีไม่มาก และในบรรดาโซลูชันโอเพนซอร์ส หลายตัวก็เก่าและแทบไม่ได้รับการดูแลแล้ว
- แพลตฟอร์มยืนยันตัวตนแบบบริการมีความเสี่ยงจากผู้ขาย และอาจเจอปัญหาแบบเดียวกับ Clerk ซ้ำอีก
- Val Town ไม่อยากสร้างระบบยืนยันตัวตนขึ้นเองตั้งแต่ต้นแล้วเปิดช่องให้เกิดช่องโหว่ใหม่ ๆ ที่น่าอาย แต่ก็ไม่อยากโยนความรับผิดชอบมากเกินไปให้ผู้ให้บริการ
- โดยเฉพาะอย่างยิ่ง ได้เกิดหลักเกณฑ์ชัดเจนว่าจะไม่กลับไปเชื่อถือการจัดการเซสชันของบุคคลที่สามอีก
การเลือก Better Auth และวิธีการย้าย
- Better Auth ตอบโจทย์หลายข้อด้วยคุณภาพโค้ดที่สูง การผสานกับหลายเฟรมเวิร์กที่ดี และการเป็นโปรเจกต์โอเพนซอร์สอิสระที่ใช้งานจริงได้
- Better Auth เองก็ยังเป็นโค้ดเบสขนาดใหญ่และซับซ้อนที่ส่วนใหญ่พัฒนาโดยบริษัทเดียว จึงยังมีความเสี่ยงจากผู้ขายอยู่
- แต่การพึ่งพาว่าบุคคลที่สามต้องออนไลน์ตลอดเวลาเพื่อให้เซสชันและการยืนยันตัวตนทำงานได้ ได้หายไปแล้ว
- AuthKit ของ WorkOS ก็เป็นตัวเลือกที่แข็งแกร่งและลื่นไหลมาก แต่หลังผ่านผู้ให้บริการมาแล้วสองราย ทางเลือกที่ทำงานได้อย่างอิสระและมีแกนหลักเป็นโอเพนซอร์สจึงสำคัญกว่า
- แดชบอร์ดและส่วนเสริมแบบเสียเงินของ Better Auth ก็ตรงกับความต้องการของ Val Town
- Val Town จัดการข้อมูลทั้งหมดเอง และปลั๊กอินจะเปิด API บนเว็บไซต์ Val Town เพื่อให้แดชบอร์ดของ Better Auth ดึงข้อมูลและทำงานจัดการผู้ใช้แบบเบื้องต้นได้
- บริการเสียเงินของ Better Auth ที่ชื่อ “Infrastructure” ในรูปแบบการใช้งานของ Val Town แทบจะเป็นแบบ stateless และไม่เกี่ยวข้องกับการจัดการเซสชัน
- ในช่วงย้ายระบบ Val Town รองรับทั้ง Better Auth และ Clerk พร้อมกันประมาณ 2 สัปดาห์
- endpoint ทั้งหมดที่เกี่ยวกับการยืนยันตัวตนยอมรับคุกกี้ทั้งสองแบบ และหน้าเข้าสู่ระบบจะออกเซสชัน Better Auth ทำให้ผู้ใช้ค่อย ๆ ย้ายไปยัง Better Auth ได้ทีละน้อย
- เพราะเป็นงานที่เกี่ยวข้องกับความปลอดภัย จึงต้องอ่านโค้ดทุกส่วนอย่างละเอียด เขียนใหม่ และทดสอบใหม่ โดยโค้ดยืนยันตัวตนแบบ Better Auth ล้วนในขั้นสุดท้ายเขียนขึ้นเองทั้งหมด
- Better Auth ยังเข้ากันได้ดีกับ Vals และหากต้องการเพิ่มการยืนยันตัวตนให้โค้ด Val Town ก็สามารถใช้ Better Auth starter template ได้
บทเรียนที่ยังเหลืออยู่
- Uptime ของผู้ให้บริการต้นทางส่งผลโดยตรงต่อ uptime ของบริการตนเอง ดังนั้นต้องประเมินอย่างรอบคอบว่ากำลังเปิดรับความเสี่ยงนั้นมากแค่ไหน
- แม้ผลิตภัณฑ์หนึ่งจะเหมาะกับกรณีใช้งานจำนวนมากและประสบความสำเร็จอย่างมาก ก็อาจไม่เหมาะกับปัญหาเฉพาะบางแบบ
- โลกของซอฟต์แวร์เปลี่ยนเร็ว และทางออกที่เหมาะสมซึ่งยังไม่มีในวันนี้ อาจปรากฏขึ้นในอีกหนึ่งปีข้างหน้าก็ได้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
อยากให้คนที่ฉลาดกว่าผมอธิบายทีว่าทำไมเราต้องยก ตาราง users ของ Postgres ให้ผู้ให้บริการภายนอกดูแล
ผมไม่เข้าใจจริง ๆ ว่าการดูแลตารางบน Hetzner VM ด้วยตัวเองมันยากตรงไหน ไม่ใช่เรื่องชำระเงินสักหน่อย ก็แค่ข้อมูลไม่กี่ฟิลด์เอง
มันจะมีทั้ง SSO, SAML, SCIM, OIDC, OAuth, การยืนยันตัวตนสองขั้นตอน, passwordless auth, verification token และแต่ละฟีเจอร์ก็ยังต้องรองรับการเชื่อมต่อดัดแปลงกับระบบยอดนิยมต่าง ๆ อีก ที่บริษัทเราอยู่พักหนึ่ง เวลาของวิศวกรซัพพอร์ตครึ่งหนึ่งหมดไปกับการแก้ปัญหา SSO จุกจิกของระบบ auth ที่ทำเอง
แบบนั้นคุณก็โยนทุก requirement ที่รู้สึกว่า “ทำเองก็ไม่เพิ่มคุณค่าอะไร” เข้าไปในกล่องดำวิเศษนั้นได้หมด
แต่ถ้าเป็นระดับ enterprise มันอาจต่างออกไป
ผมคือ Bereket จาก Better Auth ผมเริ่มทำ Better Auth ก็เพื่อแก้ปัญหานี้ตรง ๆ นี่แหละ แล้วหลังจากนั้นมันก็กลายเป็นบริษัท
ดีใจเสมอที่เห็นคนอื่นได้ประโยชน์แบบเดียวกัน ยังมีงานต้องทำอีกเยอะ เลยอยากรู้ว่าควรปรับปรุงอะไรบ้าง
มันแย่ยิ่งกว่าแค่คำกล่าวที่ว่า “บทเรียนอันหนักหนาจากการสร้างระบบซับซ้อนคือ ความน่าเชื่อถือของมันจะเท่ากับค่าต่ำสุดของชิ้นส่วนสำคัญแต่ละชิ้น”
ในความเป็นจริง มันคือ ผลคูณของ availability ของทุกองค์ประกอบบน critical path ต่างหาก ถ้าซอฟต์แวร์, ชั้น auth และ cloud provider มี availability อย่างละ 99% และถ้าตัวใดตัวหนึ่งล่มแล้วบริการล่มตาม สุดท้าย availability รวมจะเหลือแค่ 97% เท่านั้น ถ้ามีองค์ประกอบแบบนั้น 11 ตัว คุณจะไม่เหลือ “เลข 9” ในค่า availability เลย นั่นจึงเป็นเหตุผลว่าทำไมการลดจำนวนองค์ประกอบและเลือกวิธีที่น่าเชื่อถือสูงกว่าถึงสำคัญ และก็ดีแล้วที่ทีมนี้เลือกทางนั้น
ผมก็อ่านโพสต์ migration จาก Supabase ก่อนหน้านี้อย่างสนุกเหมือนกัน (https://blog.val.town/blog/migrating-from-supabase)
บทความที่ซื่อตรงและดีเกี่ยวกับการตัดสินใจทางวิศวกรรมระยะยาวมีน้อยมาก เลยหวังว่าจะเขียนบล็อกต่อไปเรื่อย ๆ
ผมเพิ่งผ่านกระบวนการคล้าย ๆ กันมาไม่นาน เริ่มจาก Stack Auth แต่เอาไปใช้ production ไม่ได้เพราะ rate limit โหดมากและประสิทธิภาพแย่ ต่อให้ไม่ติด rate limit มันก็ยังช้าอยู่ดี
หลังจากนั้นย้ายไป WorkOS AuthKit ซึ่งทำงานได้ดีกว่ามากและรองรับฟีเจอร์ enterprise ที่มีประโยชน์ด้วย ถึงอย่างนั้นถ้าเป็นโปรเจกต์ใหม่ ผมก็ยังเอนเอียงไปทาง Better Auth การซิงก์สถานะระหว่างผู้ให้บริการ auth ภายนอกกับสถานะผู้ใช้ของตัวเองเป็นแหล่งเพาะบั๊กชัด ๆ และการเก็บสถานะไว้กับผู้ให้บริการ auth ให้น้อยที่สุดก็ช่วยได้ แม้ว่าจะยังเหลืออยู่บ้างก็ตาม การรีเฟรช JWT access token ทุกไม่กี่นาทีก็เป็นอีกแหล่งเพาะบั๊กหนึ่ง และถ้าคุณควบคุม auth เองจริง ๆ ก็พูดตรง ๆ ว่าไม่จำเป็นต้องทำแบบนั้นเลย WorkOS ก็ไม่ได้มี API แบบครบถ้วน และถูกออกแบบบนสมมติฐานว่าหนึ่งบัญชีที่คิดเงินจะมีหนึ่งผลิตภัณฑ์กับจำนวน environment แบบตายตัว (staging, production และอีกหนึ่งอันถ้าขอซัพพอร์ต) เรื่องอย่าง redirect URL ก็ต้องไปใส่ allowlist ใน dashboard ด้วย และดูเหมือนไม่มีวิธีที่ agent จะจัดการสิ่งนี้ได้ง่าย ๆ ผมมองว่าการเอา auth ไปจ้างข้างนอกไม่ค่อยสมเหตุสมผล ยิ่งแยก state ไปหลายบริการน้อยเท่าไร ปัญหาก็ยิ่งน้อยลงเท่านั้น บางกรณีอย่างระบบชำระเงินที่เลี่ยงไม่ได้ หรือกรณีที่ต้องใช้ฐานข้อมูลเฉพาะทางเพราะเรื่องประสิทธิภาพก็เข้าใจได้ แต่สำหรับ auth ถ้ามีไลบรารีดี ๆ ก็ไม่มีเหตุผลจริง ๆ ที่ต้องทำแบบนั้น มีคนบอกว่าการใช้บริการช่วยให้เริ่มได้เร็วกว่า แต่ปัญหาที่ผมเจอกับบริการ auth ไม่ได้เกี่ยวกับสเกลสูงเลย และส่วนใหญ่ก็เกิดตั้งแต่ก่อนเปิดตัวแล้ว
ผมใช้ Clerk อยู่และค่อนข้างไม่พอใจ มันไม่มี RBAC ที่ดีพอ
บทบาทถูกผูกกับองค์กร ไม่ได้ผูกกับตัวผู้ใช้เอง เลยไม่มีแนวคิดแบบ global admin และต้องอ้อมไปเก็บคู่คีย์-ค่าแบบตามใจใน metadata ช่วงไม่กี่สัปดาห์หรือไม่กี่เดือนที่ผ่านมา มันก็ล่มหลายครั้ง และทุกครั้งแอปทั้งตัวก็พังตามไปด้วย ต่อไปจะใช้อีกไหมคงต้องคิดหนัก
รู้สึกโชคดีมากที่ตอนแรกเลือก Lucia ไลบรารีนี้ยุติไปแทบจะสมบูรณ์แล้ว และแทนที่ด้วยเอกสารกับ utility เล็ก ๆ สำหรับการจัดการและโฮสต์ auth ด้วยตัวเอง
auth มักถูกนำเสนอเหมือนเป็นเรื่องใหญ่และน่ากลัวที่ดูแลเองไม่ได้เสมอ แต่พอใช้เวลาสักสัปดาห์เรียนรู้เรื่องความปลอดภัยกับการทำ salting ขั้นพื้นฐาน ผมก็มั่นใจขึ้นมากกับภาพรวมว่าทั้งหมดมันทำงานอย่างไร
จำได้ว่าตอนที่พวกเขาเลิกทำไลบรารีแล้วเปลี่ยนมันเป็นสื่อการเรียนรู้สำหรับการทำ auth เองตั้งแต่ต้น เป็นการตัดสินใจที่ยอดเยี่ยม และผมก็เคารพผู้เขียนมาก
จะบอกว่าการ เปรียบเทียบ Clerk กับ Better Auth แทบจะไม่ยุติธรรมก็ได้ อันหนึ่งเป็นบริการ อีกอันเป็นไลบรารี มันเหมือนเอาแอปเปิลไปเทียบกับส้ม
บริการ third-party ทุกตัวที่รวมเข้ากับสแตกเป็นภาระด้านความรับผิดชอบ และไลบรารีก็เป็นแบบนั้นเหมือนกันแต่เบากว่า ตอนนี้ถึงเวลาแล้วที่บริการอีกมากจะถูกแทนที่ด้วยไลบรารี และ Better Auth ก็แสดงให้เห็นแนวทางนั้นได้ดี เป็นไลบรารีที่เชื่อมเข้ากับ frontend, backend และฐานข้อมูลได้โดยตรง ซึ่งดีมาก
บทความของ Tom น่าอ่านเสมอ
ยังมีใครจำ Auth0 กับ passportjs ได้ไหม? โลกของบริการ auth เปลี่ยนไปไม่สิ้นสุด แต่ก็คงเลี่ยงไม่ได้เพราะมาตรฐานเองก็ยังเปลี่ยนต่อเนื่อง