Turso 構成で、認証をつけるなら Clerk

以前の記事

https://qiita.com/pianopia/items/c3f8b5a9f30d7ee4e33c

またも以前の記事に関連していますが、
Tursoの場合、認証基盤がないのでAuth.js（旧NextAuth）で作ろうと書いていたのですが、
Turso公式がClerkを推していました。

https://turso.tech/blog/why-we-transitioned-to-clerk-for-authentication

こちらも実装が非常に楽ではあったのですが、
authMiddlewareがdeprecatedになりClaude3.7では知識がインプットされていなかったため、
新しい方のclerkMiddlewareによる実装を記載します。

## Clerkの認証設定

srcディレクトリがあれば、その直下にmiddleware.ts
なければ、プロジェクトルートにmiddleware.tsを作成します。

```Typescript:src/middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware()

// 認証したい
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

// パブリックルートの定義
const isPublicRoute = createRouteMatcher([
  "/",
  "/public"
]);

export default clerkMiddleware(async (auth, request) => {
  if (!isPublicRoute(request)) {
    await auth.protect()
  }
})

export const config = {
  matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
}; 
```


## ログイン画面の例

```Typescript:/sign-in/page.tsx
'use client';

import { SignIn } from '@clerk/nextjs';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useClerk } from '@clerk/nextjs';

export default function SignInPage() {
  const router = useRouter();
  const { session } = useClerk();

  // ユーザーが既にログインしている場合はマイページにリダイレクト
  useEffect(() => {
    if (session) {
      router.push('/mypage');
    }
  }, [session, router]);

  return (
    <div className="flex min-h-screen flex-col items-center justify-center p-4">
      <div className="w-full max-w-md">
        <h1 className="mb-8 text-center text-3xl font-bold text-white">ログイン</h1>
        <div className="rounded-lg bg-white/90 p-6 shadow-lg">
          <SignIn
            appearance={{
              elements: {
                formButtonPrimary: 'bg-blue-500 hover:bg-blue-600 text-sm normal-case',
                card: 'bg-transparent shadow-none',
              },
            }}
            routing="path"
            path="/sign-in"
            redirectUrl="/mypage"
          />
        </div>
      </div>
    </div>
  );
} 
```


## アカウント登録画面の例

```Typescript:/sign-up/page.tsx
'use client';

import { SignUp } from '@clerk/nextjs';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useClerk } from '@clerk/nextjs';

export default function SignUpPage() {
  const router = useRouter();
  const { session } = useClerk();

  // ユーザーが既にログインしている場合はマップページにリダイレクト
  useEffect(() => {
    if (session) {
      router.push('/mypage');
    }
  }, [session, router]);

  return (
    <div className="flex min-h-screen flex-col items-center justify-center p-4">
      <div className="w-full max-w-md">
        <h1 className="mb-8 text-center text-3xl font-bold text-white">アカウント登録</h1>
        <div className="rounded-lg bg-white/90 p-6 shadow-lg">
          <SignUp
            appearance={{
              elements: {
                formButtonPrimary: 'bg-blue-500 hover:bg-blue-600 text-sm normal-case',
                card: 'bg-transparent shadow-none',
              },
            }}
            routing="path"
            path="/sign-up"
            redirectUrl="/mypage"
          />
        </div>
        <div className="mt-4 text-center">
          <button
            onClick={() => router.push('/sign-in')}
            className="text-white underline hover:text-blue-300"
          >
            既にアカウントをお持ちの方はこちら
          </button>
        </div>
      </div>
    </div>
  );
} 
```