โปรเจกต์ตัวอย่างสำหรับ Production ที่โครงสร้างขยายง่าย ใช้ SvelteKit + Svelte 5 Runes และ Tailwind CSS v4 แยกกลุ่มหน้า Public/Auth/Admin ชัดเจน มี Service Layer, Protected Route ฝั่งเซิร์ฟเวอร์ และตัวอย่างการแสดงข้อมูลจำนวนมากด้วย Virtualization + Cursor Pagination
$state
, $derived
, $props
, ใช้ onclick
แทน on:click
)src/app.css
)$lib/services/api.ts
) รองรับ FormData
และ RequestInit
(signal
)src/
app.css # Tailwind v4 + Global styles
hooks.server.ts # อ่าน JWT จาก Cookie/Header -> ใส่ user/token ลง locals
lib/
components/ # Navbar/Footer/Admin components reuse
layouts/ # MainLayout, AdminLayout
services/
api.ts # HTTP client get/post/put/delete รองรับ FormData + RequestInit
types/ # api.ts, auth.ts, product.ts
features/
auth/
components/ # LoginForm, RegisterForm
services/
authService.ts # login/logout/register + ตั้ง/ลบ cookie token
admin/
components/
Dashboard.svelte
Users.svelte # ตัวอย่างตารางธรรมดา + filter + paging
UsersTanstack.svelte # ตาราง virtualized + infinite loading + prefetch-all
Products.svelte
CreateProduct.svelte
UpdateProduct.svelte
Setting.svelte, Profile.svelte
services/
productService.ts # CRUD สินค้า
routes/
(public)/ # หน้า public
(auth)/ # /login, /register
(admin)/
+layout.server.ts # ปกป้องทุกหน้าในกลุ่ม admin (server-side)
admin/
dashboard/+page.svelte
users/+page.svelte # เรียกใช้ Users.svelte
userstanstack/
+page.server.ts # ดึง initial + nextCursor ด้วย Prisma cursor
+page.svelte # เรียกใช้ UsersTanstack.svelte
members/
+page.server.ts # นับสถิติ (count) + initial rows (สำหรับ virtualization)
+page.svelte # การ์ดสรุป + ตาราง virtualized (ID/Name/Email/Phone/CreatedAt)
api/
users/+server.ts # Endpoint โหลดเพิ่ม users (cursor-based)
members/+server.ts # Endpoint โหลดเพิ่มสมาชิก (cursor-based)
hooks.server.ts
token
Buffer.from(..., 'base64')
และกัน token format ผิดพลาดlocals.user
; เก็บ locals.token
เสมอเมื่อพบ token(admin)/+layout.server.ts
/login?next=<path>
เมื่อ “ไม่มีทั้ง locals.user
และ locals.token
”หมายเหตุ: ให้ backend เซ็ต Cookie token
แบบ HttpOnly; Secure; SameSite=Lax
เพื่อความปลอดภัยสูงสุด
take
, orderBy
, cursor
) ส่ง initial
+ nextCursor
@tanstack/svelte-virtual
ทำ virtualization + infinite loadingUsersTanstack.svelte
(prefetch-all) สำหรับ use-case ที่ต้องการดึงครบจริงๆlib/services/api.ts
FormData
จะไม่ตั้ง Content-Type
ให้เองRequestInit
เช่น { signal }
features/admin/services/productService.ts
getProducts(page, search, limit, signal?)
, getProduct(id, signal?)
createProduct(data | FormData)
, updateProduct(id, data | FormData)
(รองรับ _method=PUT
)deleteProduct(id)
$state
+ $derived
และ AbortController
/admin/products/create
members/+page.server.ts
นับจำนวนทั้งหมด (prisma.user.count()
) แล้วส่ง stats
ให้หน้าเพจnextCursor
api/members/+server.ts
เป็น endpoint โหลดเพิ่มแบบ cursor-based และ normalize fullname
src/app.css
@import 'tailwindcss'
@layer base {
html { @apply scroll-smooth }
body { font-family: "Inter", "Anuphan", sans-serif }
}
# ติดตั้ง
npm install
# เริ่มพัฒนา
npm run dev
# สร้าง build production
npm run build
npm run preview
VITE_API_BASE_URL=https://your-api.example.com/api
DATABASE_URL=postgresql://user:pass@host:5432/db
$state
, $derived
, $props
), ใช้ onclick
แทน on:click
aria-label
, หลีกเลี่ยง href="#"
หากไม่ใช่ลิงก์จริงAbortController
token
หลัง login และค่าใน locals
ผ่าน log ของ hooks.server.ts
/api/users
, /api/members
) ว่าคืน nextCursor
ถูกต้องVITE_API_BASE_URL
และ DATABASE_URL