Architecture
หน้าตาของระบบ
หัวข้อที่มีชื่อว่า “หน้าตาของระบบ”FitTrack คือ backend หนึ่งตัว, สอง client และ managed platform อยู่ข้างล่าง
flowchart TD Flutter["Flutter app<br/>(mobile, primary)"] Svelte["Svelte web<br/>companion"] Auth{{"Supabase Auth"}} API["FastAPI<br/>(the shared backend)"] DB[("Supabase Postgres")]
Flutter -->|"sign in"| Auth Svelte -->|"sign in"| Auth Auth -->|"JWT"| Flutter Auth -->|"JWT"| Svelte Flutter -->|"REST + JWT"| API Svelte -->|"REST + JWT"| API API -->|"verify JWT"| Auth API -->|"async SQL"| DBclient ทั้งสอง ยืนยันตัวตนด้วย Supabase Auth แล้วได้ JWT กลับมา จากนั้นทุก request ที่ยิงไป backend จะพก token นั้นไปด้วย และ FastAPI เป็นสิ่งเดียวที่แตะ data — backend เป็นคน verify JWT, รัน business logic แล้วอ่านและเขียน Supabase Postgres ผ่าน connection แบบ async client ไม่เคย query database ตรง ๆ เลย
ทำไมต้องแบ่งแบบนี้
หัวข้อที่มีชื่อว่า “ทำไมต้องแบ่งแบบนี้”Supabase ใช้เป็น backend-as-a-service ล้วน ๆ ก็ได้ — client คุยตรงไปยัง Postgres ผ่าน Row-Level Security แต่ FitTrack ตั้งใจไม่ทำแบบนั้น เราวาง FastAPI ไว้ตรงกลาง ด้วยสามเหตุผล:
- มีที่เดียวสำหรับ business logic “อะไรนับเป็น personal record”, “volume รายสัปดาห์คำนวณยังไง”, “user แก้ exercise ตัวไหนได้บ้าง” — ของพวกนี้ควรอยู่ในโค้ดที่คุณเป็นเจ้าของและ test เองได้ ไม่ใช่กระจายอยู่ในแอป client สองตัวและกอง SQL policy
- สอง client, contract เดียว แอป Flutter กับ Svelte companion ต่างกันมาก แต่ทั้งคู่เรียก REST API ตัวเดียวกัน แก้ rule ครั้งเดียว client ทั้งสองได้เหมือนกันหมด นั่นคือหัวใจของโปรเจกต์นี้: สร้าง product สองรอบบน backend เดียว
- ได้สอน Python backend จริง ๆ FastAPI, async SQLAlchemy, Pydantic, auth ที่ inject ผ่าน dependency — เนื้อหาแท้ ๆ ของ production API — คือสิ่งที่ setup แบบ Supabase client-only จะข้ามไปพอดี
Supabase ยังคุ้มที่จะมีอยู่: Auth (สมัคร, เข้าสู่ระบบ, refresh token, OAuth ครบพร้อม client SDK ชั้นดี) และ managed Postgres (ไม่ต้องรันหรือ backup database เอง) คืองานจริงที่คุณไม่ต้องสร้างเอง Row-Level Security ยังเปิดไว้เป็น defense-in-depth เพื่อว่าถึง token จะหลุด database ตัวเองก็ยังบังคับเรื่อง ownership อยู่ดี
องค์ประกอบ
หัวข้อที่มีชื่อว่า “องค์ประกอบ”- Supabase Auth — ออกและ refresh JWT client ใช้ SDK ทางการ (
supabase_flutter,supabase-js); backend แค่ verify token เท่านั้น - FastAPI — backend ที่แชร์กัน verify Supabase JWT ทุก request, เป็นเจ้าของ domain logic และเป็นตัวเดียวที่อ่าน/เขียน database
- Supabase Postgres — managed database ของ FitTrack:
profiles,exercises,workouts,workout_sets - Flutter app — client หลักแบบ mobile-first: เข้าสู่ระบบ, เริ่ม workout, บันทึก set, ดู history และ progress
- Svelte web companion — dashboard ที่เบากว่าบน API เดียวกัน เน้นการอ่าน
ตรวจสอบผล
หัวข้อที่มีชื่อว่า “ตรวจสอบผล”คุณพร้อมไปต่อเมื่อตอบคำถามพวกนี้ด้วยคำพูดของตัวเองได้:
- ไล่ action “log a set” ตั้งแต่แตะบนแอป Flutter ไปจนถึง row ใน Postgres JWT ถูกสร้าง, แนบ และ verify ที่ตรงไหนบ้าง?
- ทำไม backend ถึงแค่ verify Supabase JWT แทนที่จะออก token ของตัวเอง?
- ถ้า Row-Level Security คือ defense-in-depth และ FastAPI คือ gate ตัวจริง อะไรจะพังถ้าคุณลืมเช็ก
get_current_userบน endpoint ตัวหนึ่ง — และ RLS ยังปกป้องอะไรอยู่?
ต่อไป prerequisites จะเตรียมเครื่องของคุณให้พร้อม