Dark mode
การเชื่อมต่อ Auth กับ Clerk
Clerk เป็นบริการ Authentication และ User Management ที่ใช้งานง่ายและมีฟีเจอร์ครบครันสำหรับ Next.js
การติดตั้ง
- ติดตั้ง package ที่จำเป็น:
bash
bun add @clerk/nextjs
bash
npm install @clerk/nextjs
bash
pnpm add @clerk/nextjs
bash
yarn add @clerk/nextjs
- สร้างไฟล์
.env.local
และเพิ่มค่าเหล่านี้:
bash
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_*
CLERK_SECRET_KEY=sk_test_*
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard
โครงสร้างโฟลเดอร์
my-app/
├── app/
│ ├── (auth)/
│ │ ├── sign-in/
│ │ │ └── [[...sign-in]]/
│ │ │ └── page.tsx
│ │ └── sign-up/
│ │ └── [[...sign-up]]/
│ │ └── page.tsx
│ ├── dashboard/
│ │ └── page.tsx
│ ├── api/
│ │ └── protected-route/
│ │ └── route.ts
│ └── layout.tsx
├── middleware.ts
└── .env.local
การตั้งค่า
1. ไฟล์ Middleware
สร้างไฟล์ middleware.ts
ที่ root ของโปรเจค:
ts
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({
publicRoutes: ["/", "/sign-in(.*)", "/sign-up(.*)"],
});
export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
2. ไฟล์ Layout หลัก
แก้ไขไฟล์ app/layout.tsx
:
tsx
import { ClerkProvider } from "@clerk/nextjs";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<ClerkProvider>
<html lang="th">
<body>{children}</body>
</html>
</ClerkProvider>
);
}
วิธีการใช้งาน
1. หน้า Sign In
สร้างไฟล์ app/(auth)/sign-in/[[...sign-in]]/page.tsx
:
tsx
import { SignIn } from "@clerk/nextjs";
export default function SignInPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<SignIn path="/sign-in" routing="path" signUpUrl="/sign-up" />
</div>
);
}
2. หน้า Sign Up
สร้างไฟล์ app/(auth)/sign-up/[[...sign-up]]/page.tsx
:
tsx
import { SignUp } from "@clerk/nextjs";
export default function SignUpPage() {
return (
<div className="flex min-h-screen items-center justify-center">
<SignUp path="/sign-up" routing="path" signInUrl="/sign-in" />
</div>
);
}
3. หน้า Dashboard ที่มีการตรวจสอบสิทธิ์
สร้างไฟล์ app/dashboard/page.tsx
:
tsx
import { SignOutButton } from "@/components/auth/sign-out-button";
import { auth } from "@clerk/nextjs";
export default function DashboardPage() {
const { userId } = auth();
if (!userId) {
return <div>กรุณาเข้าสู่ระบบก่อน</div>;
}
return (
<div className="p-8">
<div className="flex justify-between items-center mb-8">
<h1 className="text-2xl font-bold">แดชบอร์ด</h1>
<SignOutButton />
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">ข้อมูลผู้ใช้</h2>
<p>User ID: {userId}</p>
</div>
</div>
);
}
4. Components สำหรับ Sign Out
สร้างไฟล์ components/auth/sign-out-button.tsx
:
tsx
"use client";
import { useClerk } from "@clerk/nextjs";
import { useRouter } from "next/navigation";
export function SignOutButton() {
const { signOut } = useClerk();
const router = useRouter();
const handleSignOut = () => {
signOut(() => router.push("/"));
};
return (
<button
onClick={handleSignOut}
className="px-4 py-2 bg-red-500 hover:bg-red-600 text-white rounded transition-colors"
>
ออกจากระบบ
</button>
);
}
การใช้งานกับ API Routes
สร้าง API route ที่ต้องการตรวจสอบการเข้าสู่ระบบ:
Protected API Route
สร้างไฟล์ app/api/protected-route/route.ts
:
ts
import { auth } from "@clerk/nextjs";
import { NextResponse } from "next/server";
export async function GET() {
try {
const { userId } = auth();
if (!userId) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401 },
);
}
// ดึงข้อมูลจากฐานข้อมูลหรือทำการประมวลผลต่างๆ
const secretData = {
message: "นี่คือข้อมูลลับที่ต้องเข้าสู่ระบบถึงจะเห็น",
userId,
timestamp: new Date().toISOString(),
};
return NextResponse.json(secretData);
} catch (error) {
console.error("API Error:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
}
}
ตัวอย่างการเรียกใช้ API จาก Client
สร้างไฟล์ components/protected-data.tsx
:
tsx
"use client";
import { useEffect, useState } from "react";
type ProtectedData = {
message: string;
userId: string;
timestamp: string;
} | null;
export function ProtectedData() {
const [data, setData] = useState<ProtectedData>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("/api/protected-route");
if (!response.ok) {
throw new Error("ไม่สามารถดึงข้อมูลได้");
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : "เกิดข้อผิดพลาด");
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div>กำลังโหลดข้อมูล...</div>;
if (error) return <div className="text-red-500">ข้อผิดพลาด: {error}</div>;
if (!data) return null;
return (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h3 className="font-medium mb-2">ข้อมูลจาก Protected API:</h3>
<pre className="bg-white p-4 rounded overflow-auto text-sm">
{JSON.stringify(data, null, 2)}
</pre>
</div>
);
}
การตั้งค่าเพิ่มเติม
- เปลี่ยนธีม: ดูเพิ่มเติมที่
Clerk Theming
- ตรวจสอบบทบาทผู้ใช้: ใช้
auth().sessionClaims
- Custom Metadata: เพิ่มข้อมูลเพิ่มเติมให้กับผู้ใช้ผ่าน Clerk Dashboard
ข้อควรระวัง
- อย่าลืมตั้งค่า Environment Variables ให้ถูกต้อง
- ใช้
use client
สำหรับ Client Components เท่านั้น - ตรวจสอบสิทธิ์การเข้าถึงในทุก API Route ที่ต้องการความปลอดภัย