Remix で Supabase 入門 – 匿名認証を試してみる


こんにちは。Gaji-Laboの村上です。
最近、個人開発で Remix と Supabase を組み合わせたプロジェクトを進めています。
今回は、その開発中に学んだ Supabase の匿名認証を活用して、簡単なユーザー認証機能を実装する方法を紹介します。
最終的には、Supabase のダッシュボードでユーザー情報が登録されていることを確認するところまでを進めます。

使用技術

技術バージョン
node20.18.0
npm10.8.2
remix2.12.1
supabase1.204.3

Remix の準備

プロジェクトの作成

プロジェクト名は remix-supabase-anonymous とします。

npx create-remix@latest remix-supabase-anonymous
cd remix-supabase-anonymous

npm run dev コマンド実行後、http://localhost:5173/ が表示できれば OK です。

デフォルト状態から調整

/app/root.tsx/app/routes/_index.tsx から不要なものを削除し、以下のように調整します。

import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

import "./tailwind.css";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}
import type { MetaFunction } from "@remix-run/node";

export const meta: MetaFunction = () => {
  return [
    { title: "New Remix App" },
    { name: "description", content: "Welcome to Remix!" },
  ];
};

export default function Index() {
  return (
    <main className="p-6">
      <h1 className="mb-1">TOP PAGE</h1>
      <p>texttexttext</p>
    </main>
  );
}

Supabase のローカル環境を構築する

Supabase をローカルで動かすには、Docker が必要です。
Docker については、公式ガイドに従って Docker Desktop をインストールして設定してください。
ローカル環境の構築は、Supabase Local Development & CLIに則って進めます。

Supabase CLI のインストール

npm install supabase --save-dev

Supabase プロジェクトの初期化

npx supabase init
Generate VS Code settings for Deno? [y/N]
Generate IntelliJ Settings for Deno? [y/N]

Deno を使わない方は N で enter を押下してください。
これで supabase ディレクトリと設定ファイル等が作成されます。

Supabase の起動

npx supabase start

初回起動時は少し時間がかかります。
No seed files matched pattern: supabase/seed.sql から進まないときは、Docker を再起動してみてからもう一度 npx supabase start してみてください。

起動後、ターミナルに表示されるanon keyをコピーしておきます。
http://localhost:54323/ にアクセスし、Supabase のダッシュボードが表示されていれば起動確認完了です。

匿名認証を許可する

デフォルト設定では、匿名認証が無効になっています。
そのため、ローカルで開発する場合は supabase/config.toml ファイルの enable_anonymous_sign_instrue に変更します。

# Allow/disallow anonymous sign-ins to your project.
enable_anonymous_sign_ins = true

設定を変更後、Docker を再起動します。

npx supabase stop
npx supabase start

Supabase クライアントを作成

パッケージをインストール

npm install @supabase/ssr @supabase/supabase-js

環境変数を設定

.env.local を作成して、SUPABASE_URL と SUPABASE_ANON_KEY を設定します。
SUPABASE_ANON_KEY は、コピーしておいた anon key を使います。

SUPABASE_URL=http://127.0.0.1:54321
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cXXXXXXXX

Supabase クライアントを作成する supabase.server.ts を準備

import { createServerClient, parseCookieHeader, serializeCookieHeader } from "@supabase/ssr";

/**
 * Supabaseクライアントを作成する関数。
 * 
 * @param {Request} request - クライアントからのリクエストオブジェクト。
 * @returns {{ supabase: any, headers: Headers }} Supabaseクライアントとヘッダーオブジェクトを含むオブジェクト。
 * 
 * @example
 * ```typescript
 * const { supabase, headers } = supabaseClient(request);
 * ```
 */
export const supabaseClient = (request: Request) => {
  const headers = new Headers();

  const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
    cookies: {
      getAll() {
        return parseCookieHeader(request.headers.get('Cookie') ?? '')
      },
      setAll(cookiesToSet) {
        cookiesToSet.forEach(({ name, value, options }) =>
          headers.append('Set-Cookie', serializeCookieHeader(name, value, options))
        )
      },
    },
  })

  return { supabase, headers };
}

ログイン・ログアウトの実装

ログイン機能の実装

ログイン機能の実装のために、app/routes/signin.tsx ファイルを作成します。

import { type ActionFunctionArgs, redirect } from "@remix-run/node";
import { supabaseClient } from "~/services/supabase.server";

export const action = async ({ request }: ActionFunctionArgs) => {
  const { supabase, headers } = supabaseClient(request);

  // Supabase で匿名ログインを実行
  const { error } = await supabase.auth.signInAnonymously();

  if (error) {
    return { error: error.message };
  }

  return redirect("/", {
    headers,
  });
};

ログアウト機能の実装

ログアウト機能の実装のために、app/routes/signout.tsx ファイルを作成します。

import { type ActionFunctionArgs, redirect } from "@remix-run/node";
import { supabaseClient } from "~/services/supabase.server";

export const action = async ({ request }: ActionFunctionArgs) => {
  const { supabase, headers } = supabaseClient(request);

  // Supabase でログアウトを実行
  const { error } = await supabase.auth.signOut();

  if (error) {
    return { error: error.message };
  }

  return redirect("/", {
    headers,
  });
};

ログイン・ログアウトの組み込み

app/routes/_index.tsx にログインボタンとログアウトボタンを組み込みます。
ログインしている時は、Welcome, {user.id}と表示されるようになっています。

import { type MetaFunction, type LoaderFunctionArgs } from "@remix-run/node";
import { Form, json, useLoaderData } from "@remix-run/react";
import { supabaseClient } from "~/services/supabase.server";

export const meta: MetaFunction = () => {
  return [
    { title: "New Remix App" },
    { name: "description", content: "Welcome to Remix!" },
  ];
};

export const loader = async({ request }: LoaderFunctionArgs) => {
  const { supabase } = supabaseClient(request);
  const { data: { user } } = await supabase.auth.getUser();

  return json({ user });
}

export default function Index() {
  const { user } = useLoaderData<typeof loader>();
  return (
    <main className="p-6">
      <h1 className="mb-1">TOP PAGE</h1>
      <p>texttexttext</p>
      <Form action="/signin" method="post">
        <button type="submit">ログイン</button>
      </Form>
      <Form action="/signout" method="post">
        <button type="submit">ログアウト</button>
      </Form>
      {user && <p>Welcome, {user.id}</p>}

    </main>
  );
}

http://localhost:5173/
ログインボタンを押下して、下記のように表示されればログイン成功です。

Supanase のダッシュボードで確認

http://localhost:54323/project/default/auth/users にアクセスします。
http://localhost:5173/ で表示されていた id に該当する user が登録されていることが確認できます。

以上で匿名認証の実装は完了です。

参考

Creating a Supabase client for SSR | Supabase DOCS
Anonymous Sign-Ins | Supabase DOCS

最後に

今回紹介した匿名認証機能をベースに、メール認証や OAuth 認証など、他の認証方法も試してみると、より実践的なアプリケーションを構築することができます。
Remix と Supabase の組み合わせは MVP 開発やスピード感の求められるプロジェクトでは選択肢のひとつになりそうです!

Gaji-Labo では、Remix をはじめとしたフロントエンド技術を活用し、スタートアップのプロダクト開発を支援していますので、フロントエンド開発で課題があればお気軽にお問い合わせください。

Gaji-Labo フロントエンドエンジニア向けご案内資料

Gaji-Labo は Next.js, React, TypeScript 開発の実績と知見があります

フロントエンド開発の専門家である私たちが御社の開発チームに入ることで、バックエンドも含めた全体の開発効率が上がります。

「既存のサイトを Next.js に移行したい」
「人手が足りず信頼できるエンジニアを探している」
「自分たちで手を付けてみたがいまいち上手くいかない」

フロントエンド開発に関わるお困りごとがあれば、まずは一度お気軽に Gaji-Labo にご相談ください。

オンラインでのヒアリングとフルリモートでのプロセス支援にも対応しています。

Next.js, React, TypeScript の相談をする!

投稿者 Sena Murakami

アシスタントフロントエンドエンジニア。 Webマーケティング会社でマークアップエンジニアとして経験を積み、Gaji-Laboに入社。デザインの意図を汲み取ったマークアップが得意です。チームを俯瞰してリードできるエンジニアになることが目標です。趣味はバドミントンやバレーボール、キャンプなど。