☁️

Clerkとsupabaseを使いテキストと画像を投稿できるような機能を作る

2024/11/30に公開

こんにちは

webアプリ開発でclerkとsupabaseを使い記事と画像をuploadできるような機能を作ったのでアウトプットします。
最も公式ドキュメントを少し弄っただけですが...日本語向けにはあまりなさそうなので書き記しておきます。
二回目の初投稿なので拙いと思いますがご容赦ください。

Clerkとは

爆速で認証機能を実装できるやばいBaaSサービス

supabaseとは

firebaseの代替として注目されているBaaSサービス
一応supabaseにもAuth機能はありますがそっちはまだ試せてなく...しかし認証機能だけならClerkが爆速だと思いますので多分この組み合わせが一番早いと思います。

環境や使用する技術

NextJS:15.0.3
Clerk (認証)
supabase (DB,Storage)

Clerkの設定をしよう

ClerkのトップページでGitかGoogleでログインしたら

このようなページになると思います。
そうなんです。トグルをクリックするだけでoAuth認証を付けれます!爆速すぎる!
今回はGoogleとDiscordにしましょうか。
Next.JSの導入は省略します。

次の画面に進んだら手順が書かれてますので手順に従ってコピペしていってください。
layout.tsxだけimport { ClerkProvider } from '@clerk/nextjs'だけimportし、<ClerkProvider>で包んでください。
因みにまだ実験段階のようですが日本語にすることもできます。
その場合は

npm install @clerk/localizations

をインストールし、下記のlayout.tsxに

layout.tsx
import { jaJP } from '@clerk/localizations'
{/*中略*/}
    <ClerkProvider>
        <ClerkProvider localization={jaJP}>{/*日本語対応させたないならこちら*/}
          <html lang="en">
            <body>
              {children}
            </body>
          </html>
        </ClerkProvider>

開発サーバーは立ち上げなくてもいいと思います。

終わったらこちらのボタンをクリックしてまた引き続きコピペしていってください。
そしてすべて終わって開発サーバーを立ち上げると

できましたね。何もCSS当ててないので隅に寄ってますがまぁいいでしょう。

googleかdiscordでログインしたらNextJSのデフォルトのページに飛ぶと思います!
ただこのままだとログアウトできないので適当な場所に<UserButton/>を追記しておきましょう。

page.tsx
import Image from "next/image";
import styles from "./page.module.css";
import { SignedIn, UserButton } from '@clerk/nextjs';

export default function Home() {
  return (
    <div className={styles.page}>
      <UserButton />
      <main className={styles.main}>


これで認証周りは一段落ですね!

supabaseを設定しよう

続いてsupabaseの設定を行います。
supabase

GitHubなどでログインしdashboardに行き、NewProjectでプロジェクトを作成してください。

ここからはClerk側の公式ドキュメントを参考にしてください。
ほんとは一から手順を説明したいのですがそこまで時間がなく...

ただ画像をStorageに保存した際に発行されるPathを保存するためのカラムを追加する必要がある為、二番目のステップで

create table tasks(
    id serial primary key,
    name text not null,
    user_id text not null default requesting_user_id(),
    image_url text not null
);

-- Enable RLS on the table
alter table "tasks" enable row level security;

と、クエリしてください。
また3番目の手順ではDELETEpolicyもSELECTと同様の手順で設定してください。

8番まで進めましたら一旦ストップしましてStorageのバケットを作っていきます。
supabaseの左のメニューからStorageを選びNewBacketから新規作成しましょう。

Public bucketをオンにしましょう。
Additional configurationからは送信時の容量の制限やファイル形式の制限ができるみたいです。
容量はデフォルトだとすごく小さいので5MBくらいにしておきましょう。

そしてPoliciesをクリックしPolicy設定します。
Policy name にAllow authenticated uploads
Allowed operationでINSERTにチェック
Target rolesにauthenticated
Policy definitionに

(bucket_id = 'tasks_image'::text)

を張り付けてreviewをクリックし保存しましょう。

そして私のリポジトリにdemoがあり、そこに私がテストで作ったapp/page.tsxを公開していますので
コピペなりなんなりしてください。
https://github.com/slum-kuraudo/clerk-supabaseDB-Storage-demo
尚、material UIを使ってwebアプリ開発しているので

npm install @mui/material @emotion/react @emotion/styled --legacy-peer-deps
npm install @mui/icons-material --legacy-peer-deps      

を実行してください。
--legacy-peer-depsオプションはmaterial UIがreact19に対応していなかったのでつけました。
バージョンに寄るのでお好みで。
material UIについては今後何かしらで触れたいと思います。

page.tsx
    const fileName = `${crypto.randomUUID()}`

    const { error: upError } = await client.storage
      .from('tasks_image')
      .upload(fileName, imageFile)

randomUUIDを使ってローカルのファイル名をそのままStorageにあげないようにし.uploadで引数として渡してあげます。

page.tsx
    const { data: urlData } = await client.storage
      .from('tasks_image')
      .getPublicUrl(fileName)

    await client.from('tasks').insert({
      name,
      image_url: urlData?.publicUrl,
    })

getPublicUrlでStorageに保存したパスを取得し、insert時にimage_urlに挿入できるようにしました。


実際に動かすとこうしてテキストと画像を表示させることができます。
ここから応用が色々効きそうですね。

まとめ

意外と簡単にできたと思います。

途中で飽きたのでどこか説明が抜けてるところがあるかもしれません。

ほなまた

Discussion