💬

Supabase Realtime APIでチャットアプリを作る

に公開

SupabaseのRealtime APIを使用して、リアルタイムチャットアプリを実装します。Realtime APIは、接続しているクライアント間でメッセージを送受信できるAPIです。

リポジトリ: https://github.com/k-logic563/supabase-chat-app

使用技術

主要パッケージ:

{
  "vite": "^5.3.4",
  "vue": "^3.4.31",
  "typescript": "^5.2.2",
  "tailwindcss": "^3.4.6",
  "@supabase/supabase-js": "^2.44.4"
}

フレームワーク: Vite + Vue + TypeScript

アプリケーション概要

主な機能

  • 匿名ログインで利用可能
  • 他ユーザーの書き込みがリアルタイムで自動更新される

デモ

実装手順

1. データベース設定

messagesテーブルを作成します。user_idauth.users.idを外部キーとして参照します。

create table
  public.messages (
    id bigint generated by default as identity,
    message text not null,
    created_at timestamp with time zone not null default now(),
    user_id uuid not null default gen_random_uuid (),
    constraint messages_pkey primary key (id),
    constraint messages_user_id_fkey foreign key (user_id) references auth.users (id) on update cascade on delete cascade
  ) tablespace pg_default;

RLS(Row Level Security)の設定:

  • RLSを有効化
  • selectinsertに対してauthenticatedユーザーのみアクセス可能なポリシーを設定

2. 匿名ログイン画面

ユーザーを識別するために匿名ログインを実装します。signInAnonymously関数を使用します。

<!-- views/login.vue -->
<script setup lang="ts">
const handleSigninWithAnonymous = async () => {
  try {
    await supabaseClient.auth.signInAnonymously();
  } catch (e) {
    console.log(e);
  }
}
</script>

3. 認証状態の管理

onAuthStateChange関数でユーザーの認証状態を監視し、適切なページにリダイレクトします。

<!-- guards/AuthGuard.vue -->
<script setup lang="ts">
const { data: authListener } = supabaseClient.auth.onAuthStateChange(
  async (_: unknown, session) => {
    const isLoginPage = route.path === '/login';
    
    // セッション中にログインページにアクセスした場合、ホームへリダイレクト
    if (session && isLoginPage) {
      await router.push('/');
      return;
    }
    
    // セッションがない場合、ログインページへリダイレクト
    if (!session) {
      await router.push('/login');
      return;
    }
  }
);

onBeforeUnmount(() => {
  authListener.subscription.unsubscribe();
});
</script>

4. Realtime APIの実装

channel関数でチャネルを作成し、messagesテーブルへのINSERTイベントを監視します。新しいメッセージが追加されると自動的にfetchMessageDataが実行され、画面が更新されます。

<!-- views/Home.vue -->
<script setup lang="ts">
const fetchMessageData = async () => {
  try {
    const { data } = await supabaseClient
      .from('messages')
      .select();
    if (data) {
      messageData.value = data;
    }
  } catch (e) {
    console.error(e);
  }
}

// 初回データ取得
fetchMessageData();

// Realtimeチャネルの設定
const channel = supabaseClient
  .channel('chat')
  .on(
    'postgres_changes',
    { event: 'INSERT', schema: 'public', table: 'messages' },
    fetchMessageData
  )
  .subscribe()

// コンポーネント破棄時にチャネルを解除
onBeforeUnmount(() => {
  channel.unsubscribe();
});
</script>

5. チャット一覧画面の実装

ログインユーザーのメッセージは右寄せで緑色の背景、他のユーザーのメッセージは左寄せで灰色の背景で表示します。

<!-- views/Home.vue -->
<script setup lang="ts">
const userId = ref<string>('');

onMounted(async () => {
  // 現在ログイン中のユーザーIDを取得
  const { data } = await supabaseClient.auth.getUser();
  if (data.user) {
    userId.value = data.user?.id;
  }
});
</script>

<template>
  <div class="max-w-[768px] mx-auto py-[32px]">
    <div class="h-[calc(100vh-64px)] grid grid-rows-[auto_1fr_auto]">
      <main class="p-4 bg-gray-200">
        <ul v-if="messageData.length" class="flex flex-col gap-y-4 items-start">
          <li
            v-for="data in messageData"
            :key="data.id"
            :class="[
              'rounded-full',
              'px-4',
              'py-2',
              data.user_id === userId ? 'bg-green-400 ml-auto' : 'bg-gray-100'
            ]"
          >
            {{ data.message }}
          </li>
        </ul>
      </main>
    </div>
  </div>
</template>

まとめ

Supabase Realtime APIを使用することで、簡単にリアルタイムチャットアプリを実装できます。

ポイント:

  • Realtime機能を使うにはテーブルでRealtime onを有効化
  • channelでイベントを監視し、データベースの変更を検知
  • コンポーネント破棄時にunsubscribe()を忘れずに実行

参考

公式 Realtime API
https://supabase.com/docs/guides/realtime

Discussion