この
個人の
とは
この
- 会員だけが
閲覧できる ブログ記事の 公開 - 有料記事の
公開 (会員だけ購入可能) - 今後
コメント機能を 実装するので、 その ときに ユーザ名と プロフィール画像を 表示 - スポンサー機能
- UI の
カスタマイズを 可能に する (何が 良いかは 考え中)
選んだ 理由
Clerk を認証・認可の
決め手と
そして、
ところが、
な
法
実装方具体例と
公式ドキュメントに<SignIn />
page.tsx
に
import { SignIn } from "@clerk/nextjs";
const Page = () => ( <SignIn />);
export default Page;
これでも
"use client";
import { SignIn as ClerkSignIn } from "@clerk/nextjs";import { dark } from "@clerk/themes";import { useTheme } from "@kkhys/ui";import React from "react";
export const SignIn = () => { const { theme = "system" } = useTheme();
return ( <ClerkSignIn appearance={{ baseTheme: theme === "dark" ? dark : undefined, }} /> );};
余談だが、
な
Webhook を 処理する
Inngest でClerk では
Clerk では
その後、
import { createUser, deleteUserByClerkId } from "#/app/(auth)/_lib/actions";import { getUserByClerkId } from "#/app/(auth)/_lib/queries";import type { ClerkWebhookUser } from "#/app/(auth)/_types";import { inngest } from "#/lib/inngest";
export const syncCreatedUser = inngest.createFunction( { id: "sync-created-user-from-clerk" }, { event: "clerk/user.created" }, async ({ event }) => { const { id, email_addresses, primary_email_address_id } = event.data; const email = getPrimaryEmailAddress({ email_addresses, primary_email_address_id, });
console.log("Syncing created user", { id, email });
await createUser({ clerkId: id }); },);
export const syncUpdatedUser = inngest.createFunction( { id: "sync-updated-user-from-clerk" }, { event: "clerk/user.updated" }, async ({ event }) => { const { id, email_addresses, primary_email_address_id } = event.data; const email = getPrimaryEmailAddress({ email_addresses, primary_email_address_id, });
const user = await getUserByClerkId(id);
if (!user) { await createUser({ clerkId: id }); }
console.log("Syncing updated user", { id, email }); },);
export const syncDeletedUser = inngest.createFunction( { id: "sync-deleted-user-from-clerk" }, { event: "clerk/user.deleted" }, async ({ event }) => { const { id, email_addresses, primary_email_address_id } = event.data; const email = getPrimaryEmailAddress({ email_addresses, primary_email_address_id, });
console.log("Syncing deleted user", { id, email });
await deleteUserByClerkId(id); },);
const getPrimaryEmailAddress = ({ email_addresses, primary_email_address_id,}: Pick< ClerkWebhookUser["data"], "email_addresses" | "primary_email_address_id">) => email_addresses.find(({ id }) => id === primary_email_address_id) ?.email_address ?? "no email";
具体的には
syncCreatedUser
: user テーブルに新規レコードを 追加 syncUpdatedUser
: user テーブルに対象ユーザの レコードが なければ 追加。 現状は 更新項目が ないため処理なし syncDeletedUser
: user テーブルのdeletedAt
に実行日時を 記録 (論理削除)
DB は
user
テーブルの
import { sql } from "drizzle-orm";import { pgTable } from "drizzle-orm/pg-core";
export const User = pgTable("user", (t) => ({ id: t.uuid().notNull().primaryKey().defaultRandom(), clerkId: t.varchar({ length: 255 }).notNull().unique(), deletedAt: t.timestamp(), createdAt: t.timestamp().defaultNow().notNull(), updatedAt: t .timestamp() .notNull() .$onUpdateFn(() => sql`now()`),}));
最後に
import { serve } from "inngest/next";import { syncCreatedUser, syncDeletedUser, syncUpdatedUser,} from "#/app/(auth)/_lib";import { inngest } from "#/lib/inngest";
export const { GET, POST, PUT } = serve({ client: inngest, functions: [syncCreatedUser, syncUpdatedUser, syncDeletedUser],});
一見すると、
- 並行性
(Concurrency)を 制御し、 イベントが 急増した 際も API や データベースへの 負担を 効果的に 抑える ことができる - 単一の
イベントから 複数の 処理を 同時に 開始する 機能 (ファンアウト)を 備えている ため、 ワークフローの 柔軟性が 向上する - 特定の
処理を 一定 時間後に 実行する 仕組みを 簡単に 構築できる - 同じイベントの
重複実行を 防ぐ デバウンス機能が ある
こういった
ごに
さいここまで
とは
もっとも、