
こんにちは!今回は、Next.jsアプリでClerkとSupabaseを連携し、ユーザー情報をSupabaseに保存する手順をご紹介します。
Clerkは認証・ユーザー管理プラットフォームで、数行のコードでアプリにサインイン・サインアップフォームを実装できる便利なサービスです。ClerkをNext.jsアプリに実装する手順をご紹介した前回の記事は下記からご参考ください。
本記事では、Clerkを実装したNext.jsアプリでClerkのユーザー情報Clerk User IDとemailをサインイン時にSupabaseに保存する手順をご紹介します。ぜひご参考ください!
- 1. 目標
- 2. 前提
- 3. 手順
- 4. テスト
- 5. まとめ
1. 目標
本記事の目標は下記の通りです。
- Clerkのユーザー情報Primary emailとUser IDをSupabaseに保存する。
Clerkではユーザーごとに一意のID「User ID」(user_xxxx形式)が付与されます。また、メールアドレスは複数登録できますが、その中でもメインのメールアドレス「Primary email」を保存対象とします。
各ユーザー情報はClerkのダッシュボードUsersでユーザーをクリックすると確認できます。

2. 前提
2.1. 手順概要
本記事では下記の手順で実装を進めていきます。
- ステップ1:Supabaseでプロジェクト・テーブル・RLSポリシー作成
- ステップ2:ネイティブ統合設定
- ステップ3:GitHubでコードを追加・修正
- ステップ4:Vercelにデプロイ
※Clerk公式ドキュメント
2.2. ClerkとSupabaseを連携する方法
ClerkとSupabaseは「ネイティブ統合」という方法で連携します。ネイティブ統合は、Clerkが発行するJWTトークンに必要な情報(role: authenticated等)を含めSupabaseがユーザーを認識できるようにする仕組みです。
この統合設定を行うことでSupabaseがリクエスト元のClerk User IDを取得できるようになり、「本人だけが自分のデータにアクセスできる」制御を実装できるようになります。ClerkとSupabaseのダッシュボードでの数ステップの設定で完了する便利な仕組みです。
2025年4月時点では、ClerkとSupabaseの連携にはこのネイティブ統合が推奨されています。
2.3. ユーザー情報をClerkからSupabaseに渡す方法
Clerkでサインインが完了すると、Next.jsアプリ上では@clerk/nextjsのuseUser()フックを使って、ログイン中のユーザーの情報(ClerkのUser IDやPrimary email)を取得できます。例えば、Clerk User IDはuser.id、Primary emailはuser.primaryEmailAddress?.emailAddressで取得できます。
取得したユーザー情報は、Clerkが発行するJWTトークン(本人確認用トークン)と共にSupabaseクライアント@supabase/ssrを通じてSupabaseに送られます。この仕組みによりClerkのユーザー情報をSupabaseに保存します。
2.4. 保存先がauth.usersではなくpublicテーブルになる理由
今回は、Supabaseでユーザー情報を保存するためのpublic.profilesテーブルを作成し、このテーブルにデータを保存するように実装していきます。
Supabaseで標準の認証機能(Supabase Auth)を使用した場合、auth.usersテーブルにユーザー情報が保存されます。しかし、今回はSupabase AuthではなくClerkを使って認証しているため、auth.usersテーブルにユーザー情報を登保存できません。
そのため、publicスキーマ内に独自のテーブルを作成してそちらに保存するという方法で実装していきます。
3. 手順
それでは、Next.jsアプリでClerkとSupabaseを連携する手順を紹介していきます。
3.1. ステップ1:Supabaseでプロジェクト・テーブル・RLSポリシー作成
連携するSupabaseの設定を行っていきます。
3.1.1. プロジェクト作成
まずは、Supabaseで連携するプロジェクトを作成します。

3.1.2. API key・Supabase URLのコピー
プロジェクトを作成したら、API keyとSupabase URLをコピーして控えておきます。これらはVercelにデプロイする際に使用します。

3.1.3. テーブル作成
次に、ユーザー情報を保存するテーブルpublic.profilesを作成します。下記を参考に作成してください。


- Name…profiles
- Enable Row Level Security (RLS)…有効化
- Columns…下記を参考に作成してください。
| Name | Type | Default Value | Primary | その他 |
|---|---|---|---|---|
| id | uuid | gen_random_uuid() | Yes | - |
| user_id | text | - | No | Is Unique |
| text | - | No | - | |
| created_at | timestamptz | now() | No | Is Nullable |
user_idカラムにClerk User IDを、emailカラムにPrimary emailを保存します。
設定できたらテーブルが作成されているのを確認します。

3.1.4. RLSポリシー設定
次に、作成したpublic.profilesテーブルにRLSポリシーを設定します。今回設定するのは4つのポリシーです。このポリシーで、ユーザー自身のデータのみにアクセスできるようになります。

RLSはナビゲーションのAuthentication > Policies画面の「Create policy」から作成します。

各RLSポリシーでは、「public.profilesのuser_idカラムの値がこのリクエストを送ったユーザーのClerk User ID(auth.uid())と一致する」という条件を設定し、自分のデータのみを操作できるようにします。
下記を参考に4つのRLSポリシーを設定してください。
3.1.4.1. ①Users can delete their own profile
- 目的…自分のuser_idのデータだけ削除できるようにする
- Table…
public.profiles - Policy Behavior…Permissive
- Policy Command…DELETE
- Target Roles…authenticated
- 条件…
(user_id = (auth.uid())::text)

3.1.4.2. ②Users can insert their own profile
- 目的…自分のuser_idのデータだけ登録できるようにする
- Table…
public.profiles - Policy Behavior…Permissive
- Policy Command…INSERT
- Target Roles…authenticated
- 条件…
(user_id = (auth.uid())::text)

3.1.4.3. ③Users can select their own profile
- 目的…自分のuser_idのデータだけ見られるようにする
- Table…
public.profiles - Policy Behavior…Permissive
- Policy Command…SELECT
- Target Roles…authenticated
- 条件…
(user_id = (auth.uid())::text)

3.1.4.4. ④Users can update their own profile
- 目的…自分のuser_idのデータだけ編集できるようにする
- Table…
public.profiles - Policy Behavior…Permissive
- Policy Command…UPDATE
- Target Roles…authenticated
- 条件…
(user_id = (auth.uid())::text)

3.2. ステップ2:ネイティブ統合設定
次に、ClerkとSupabaseのネイティブ統合の設定を行います。
3.2.1. Clerk
まずはClerkのダッシュボードでSupabaseと統合設定を行います。下記のリンクからSupabaseネイティブ統合設定画面を開きます。
https://dashboard.clerk.com/setup/supabase
各項目を設定して「Activate Supabase integration」をクリックします。

項番2の「Enable the Supabase integration」のStatusが「Enabled」になり、項番3でClerk domainが表示されたら、URLをコピーしておきます。さらに、表示されているリンク「Supabase third-party auth settings.」をクリックします。

3.2.2. Supabase
先ほどリンクをクリックしたことで、SupabaseダッシュボードのAuthentication > Sign In / Up > 「Third Party Auth」が開いているはずです。「Add provider」をクリックし、ドロップダウンの「Clerk」をクリックします。ちなみに、Clerkの設定画面で表示されているリンクから開かないとドロップダウンリストにClerkが表示されない場合があるのでご注意ください。

Clerkとの連携設定ポップアップが表示されるので、先ほどコピーしたClerk domainを入力して「Create connection」をクリックします。

ポップアップが閉じ、「Third Party Auth」一覧にClerkが表示されればネイティブ統合完了です。

3.3. ステップ3:GitHubでコードを追加・修正
Supabaseと連携できるようにGitHubでコードを修正します。
3.3.1. リポジトリ作成
まずは、GitHubでClerkを実装したベースとなるNext.jsアプリのリポジトリを作成します。本記事では、前回の記事で作成したリポジトリをコピーして進めていきます。アプリ未作成の場合は下記を参考にClerkと連携済みアプリのリポジトリを準備してください。
リポジトリのコピーは、Zipでダウンロードして手動で新規リポジトリにアップロードするか、Gitでコピーするといった方法があります。お好みの方法でリポジトリを作成してください。
3.3.2. コードの修正
コードを修正していきます。
3.3.2.1. Supabaseライブラリインストール
まずは、Supabaseのライブラリをインストールするコードを追加します。一般的なクライアントライブラリ@supabase/supabase-jsと、Clerkとの統合でのトークン付き通信やセッション管理に必要なライブラリ@supabase/ssrをインストールします。
- 修正箇所:ファイル
package.jsonのdependencies内 - 追加コード:
"@supabase/supabase-js": "^2.39.5",、"@supabase/ssr": "^0.6.1",

3.3.2.2. Next.js v15対応
Next.jsのアップデート(v14→v15)に伴い、Clerk v6をプロジェクトで使用できるようにするために下記の修正を行います。
- 修正ファイル:
next.config.js - 修正内容:
serverActions: {},

3.3.2.3. ユーザーデータ保存
Clerkに保存されたPrimary emailとClerk User IDをSupabaseに保存するコードを追加します。
- 修正ファイル:
app/page.tsx - コード:下記のコードに差し替え
'use client'
import { useEffect } from 'react'
import { useSession, useUser } from '@clerk/nextjs'
import { createBrowserClient } from '@supabase/ssr'
export default function Home() {
const { user } = useUser()
const { session } = useSession()
function createClerkSupabaseClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_KEY!
)
}
const client = createClerkSupabaseClient()
// ✅ プロファイル保存処理
useEffect(() => {
console.log('🟡 useEffect called')
console.log('👤 user:', user)
if (!user) {
console.log('⛔ user is not ready yet')
return
}
const saveProfileToSupabase = async () => {
console.log('🚀 Executing upsert...')
const { error } = await client.from('profiles').upsert(
{
user_id: user.id,
email: user.primaryEmailAddress?.emailAddress,
},
{ onConflict: 'user_id' }
)
if (error) {
console.error('❌ Profile insert error:', error.message)
} else {
console.log('✅ Profile saved to Supabase')
}
}
saveProfileToSupabase()
}, [user])
return (
<div className="min-h-screen p-4 bg-gray-50">
<h1 className="text-2xl font-bold">Welcome</h1>
<p>プロフィールデータはサインイン時にSupabaseへ保存されます。</p>
</div>
)
}
3.4. ステップ4:Vercelにデプロイ
Vercelにデプロイします。
3.4.1. デプロイの設定
Vercelにアクセスし、「Import Git Repository」で作成したリポジトリの「Import」をクリックします。

次の新規プロジェクト設定画面では下記を参考に設定します。設定できたら「Deploy」をクリックします。


- Framework Preset…Next.js
- Environment Variables…下記の環境変数を設定。
| Key | Value |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
SupabaseのProjet Settings > Data API > Project URL |
NEXT_PUBLIC_SUPABASE_KEY |
SupabaseのAPI Settings > Project API Keys > anon public |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
ClerkのConfigure > API keys > Publishable key > Public key |
CLERK_SECRET_KEY |
ClerkのConfigure > API keys > Secret keys > default |
「Conglatulations」画面が表示されたらデプロイ完了です。「Continue to Dashboard」をクリックしてダッシュボードを開きます。

4. テスト
全ての手順が完了したので動作確認を行います。VercelのダッシュボードのProject > Visitをクリックしてアプリを開きます。

新規タブでアプリが開きます。非ログイン状態であれば/signinページが開くはずなので、サインインします。

トップページ/へ遷移しメッセージが表示されればサインイン完了です。

次に、Clerkダッシュボードで登録したユーザーを確認します。Primary emailとClerk User IDを確認します。

Supabaseのprofilesテーブルで、同じPrimary emailとClerk User IDが保存されているかを確認します。保存できていれば成功です!

なお、別のサインイン方法でサインインしたユーザー情報についても同様に保存できていました。
5. まとめ
今回は、Next.jsアプリでClerkで認証したユーザーのPrimary emailとClerk User IDをSupabaseに保存する手順を紹介しました。
ネイティブ統合を通じてClerkとSupabaseのシームレスな連携と安全なデータ管理を実現できましたね。既存の認証基盤とデータベースサービスを連携して開発効率を高めたい方は、ぜひ本記事をお役立てください!