Skip to content
Grok

การเชื่อมต่อ Auth กับ Clerk

Clerk เป็นบริการ Authentication และ User Management ที่ใช้งานง่ายและมีฟีเจอร์ครบครันสำหรับ Next.js

การติดตั้ง

  1. ติดตั้ง package ที่จำเป็น:
bash
bun add @clerk/nextjs
bash
npm install @clerk/nextjs
bash
pnpm add @clerk/nextjs
bash
yarn add @clerk/nextjs
  1. สร้างไฟล์ .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.com faviconClerk Theming
  • ตรวจสอบบทบาทผู้ใช้: ใช้ auth().sessionClaims
  • Custom Metadata: เพิ่มข้อมูลเพิ่มเติมให้กับผู้ใช้ผ่าน Clerk Dashboard

ข้อควรระวัง

  • อย่าลืมตั้งค่า Environment Variables ให้ถูกต้อง
  • ใช้ use client สำหรับ Client Components เท่านั้น
  • ตรวจสอบสิทธิ์การเข้าถึงในทุก API Route ที่ต้องการความปลอดภัย