BEMAロゴ

エンジニアの
成長を支援する
技術メディア

Better AuthでAstroの認証機能を簡単実装!SQLite構成でログインも作ってみた【2025年版】

この記事は「BEMA Lab Advent Calendar 2025Open in new tab」の5日目の記事です。
※本アドベントカレンダーの5日目の投稿となります。

はじめに

こんにちは。みなさんは Better AuthOpen in new tab というライブラリをご存知でしょうか?

Better Auth は、Web アプリケーション用に設計されたオープンソースの認証ライブラリです。フレームワークに依存しないため、さまざまな構成でその恩恵を受けることができます。

認証ライブラリといえば、Auth.jsOpen in new tab(旧 NextAuth.js)を思い浮かべる方も多いと思います。実は、その Auth.js は 2025年9月に Better Auth に吸収されましたOpen in new tab

そのため、これから新しく認証機能を実装する場合や Auth.jsOpen in new tab からの移行先を検討している場合、Better Auth が大きな選択肢になると感じています。
今回は、Better Auth を使って Astro で「登録・ログイン・ログアウト」機能を持ったシンプルな認証機能を実装してみたので、その手順を共有します。

作成する機能と環境

実装機能

  • 登録機能(メールアドレス、名前、ユーザー名、パスワード)
  • ログイン機能(メールアドレスとパスワードでログイン)
  • ログイン後のユーザー情報表示(デバッグ用)
  • ログアウト機能

使用ライブラリのバージョン

今回作成した環境は以下の通りです。

パッケージ

バージョン

Astro

5.15.2

Better Auth

1.3.33

better-sqlite3

12.4.1

実装手順

1. 必要なパッケージのインストール

すでに Astro のプロジェクトをセットアップしている前提で進めます。まだ作成されていない方は、以下の公式ドキュメントを参考に作成してください。

それでは、Better Auth を使うために必要なパッケージをインストールしていきます。
データベースに関しては、簡単な構成で作ることを目的としているため SQLite を使います。
SQLite はディスク上のデータベースファイルを読み書きするもので、ローカルのみで動作させることが可能です。

※注意 生成される auth.db ファイルにはユーザー情報が含まれるため、Git の管理対象外になるよう .gitignore に auth.db を追記しておくことを推奨します。

npm install better-auth better-sqlite3
npm install -D @types/better-sqlite3

2. Better Auth の設定(サーバーサイド)

Better Authの設定ファイルを作成します。主にログインや登録、ログアウトなどといった API を叩くために使用します。
ここで export する auth 関数はサーバーサイドでのみ使用します。

ファイル: /src/lib/auth.ts

import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
export const auth = betterAuth({
  database: new Database("auth.db"), // データベースファイル設定
  emailAndPassword: {
    enabled: true, // メールアドレスとパスワードでの登録を有効化
  },
});

次に環境変数を設定します。

必要なものは秘密鍵とベースURLです。秘密鍵は暗号化とハッシュ化に使用され、ベースURLは認証ライブラリ側で使用するために設定します。

まずは以下のコマンドなどを実行して、秘密鍵を生成します。(複雑な文字列であれば以下のコマンドで生成したものでなくても構いません)

openssl rand -base64 32

そしてプロジェクトのルートフォルダに .env ファイルを作成し、生成した秘密鍵とローカルのURLを以下のような形で貼り付けます。

BETTER_AUTH_SECRET=<生成した秘密鍵>
BETTER_AUTH_URL=http://localhost:4321 // Astroのデフォルトポートが4321のため

最後にデータベーステーブルの作成をするため、以下のコマンドを実行しデータベースを生成します。

npx @better-auth/cli generate

3. API ルートの設定

Astro で Better Auth のリクエストを処理するためのハンドラーを設定します。/api/auth/ 以下のリクエストをすべて受け取れるようにします。

これは /api/auth/ 以下のすべてのリクエストを Better Auth のハンドラーに渡すための設定です。

ファイル: /src/pages/api/auth/[...all].ts

import { auth } from "../../../lib/auth";
import type { APIRoute } from "astro";

export const prerender = false;

export const ALL: APIRoute = async (ctx) => {
  return auth.handler(ctx.request);
};

4. クライアントの設定

次にクライアント側の設定です。/src/lib/auth-client.ts というファイルに AuthClient を作成するコードを記述します。

今回は特に設定についてはいじっていないですが、プラグインなどを追加する場合はここに設定ファイルを追加していくことになります。

また、この export した authClient はクライアントサイドでのみ使用します。サーバーサイドでは使用しません。

ファイル: /src/lib/auth-client.ts

import { createAuthClient } from "better-auth/client";

export const authClient = createAuthClient();

5. メインページ(ホーム)

メインページでは認証状態に応じて表示を切り替えるような画面表示にしています。
ログイン時には「ログイン済み」という文字とログインユーザーの情報、そしてログアウトボタンを表示します。
そして未ログイン時には「サインイン」「サインアップ」の 2 つのボタンを表示するようにしています。

なお、このページではサーバーサイドで auth.api.getSession を使ってセッション情報を取得し、認証状態を判定しています。ログアウト処理はクライアントサイドの authClient.signOut を使用します。

ファイル: /src/pages/index.astro

コード スニペット

---
import { auth } from "../lib/auth";

export const prerender = false;

const session = await auth.api.getSession({
    headers: Astro.request.headers,
});
---

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>Better Auth Demo</title>
    </head>
    <body>
        <h1>Better Auth Demo</h1>
        {
            session ? (
                <>
                    {/* ログイン時の表示 */}
                    <p>ログイン済み</p>
                    <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;">
                        <strong>ユーザー情報:</strong> <br />
                        <span>ユーザー名: {session.user.username}</span><br />
                        <span>名前: {session.user.name}</span><br />
                        <span>メールアドレス: {session.user.email}</span>
                    </div>
                    <button data-id="signout">ログアウト</button>
                </>
            ) : (
                <>
                    {/* 未ログイン時の表示 */}
                    <a href="/signin">サインイン</a>
                    <a href="/signup">サインアップ</a>
                </>
            )
        }

        <script>
            import { authClient } from "../lib/auth-client";

            const signoutButton = document.querySelector<HTMLButtonElement>("[data-id='signout']");
            
            signoutButton?.addEventListener("click", async () => {
                await authClient.signOut({
                    fetchOptions: {
                        onSuccess: () => {
                            window.location.reload();
                        },
                    },
                });
            });
        </script>
    </body>
</html>

6. サインインページ

ユーザー名とパスワードでログインするシンプルなフォームを設置した静的なページを作成します。
サインインボタン押下時(フォーム送信時)に Better Auth のサインイン処理を呼び出します。

ファイル: /src/pages/signin/index.astro

コード スニペット

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>Better Auth Demo Signin</title>
    </head>
    <body>
        <h1>サインイン</h1>
        <form id="signin-form" method="post">
            <input type="text" name="email" placeholder="メールアドレス" required />
            <input type="password" name="password" placeholder="パスワード" required />
            <button type="submit">サインイン</button>
            <span style="color: red;" data-id="error-message"></span>
        </form>
        <p>アカウントをお持ちでない方は <a href="/signup">サインアップ</a></p>

        <script>
            import { authClient } from "../../lib/auth-client";

            const form = document.querySelector<HTMLFormElement>("#signin-form");
            
            form?.addEventListener("submit", async (event: SubmitEvent) => {
                event.preventDefault();
                const formData = new FormData(form);
                
                // サインイン処理
                const data = await authClient.signIn.email({
                    email: formData.get("email") as string,
                    password: formData.get("password") as string,
                });

                if (data.error) {
                    const errorMessageElement = document.querySelector<HTMLSpanElement>("[data-id='error-message']");
                    errorMessageElement!.textContent = data.error.message ?? "";
                } else {
                    window.location.href = "/";
                }
            });
        </script>
    </body>
</html>

7. サインアップページ

新規登録用のページです。こちらもフォーム送信時に Better Auth のメソッドを呼び出すだけです。

ファイル: /src/pages/signup/index.astro

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>Better Auth Demo</title>
    </head>
    <body>
        <h1>サインアップ</h1>
        <form id="signup-form" method="post">
            <input type="email" name="email" placeholder="メールアドレス" required />
            <input type="text" name="name" placeholder="名前" required />
            <input type="text" name="username" placeholder="ユーザー名" required />
            <input type="password" name="password" placeholder="パスワード" required />
            <button type="submit">サインアップ</button>
            <span style="color: red;" data-id="error-message"></span>
        </form>

        <p>既にアカウントをお持ちの方は <a href="/signin">サインイン</a></p>

        <script>
            import { authClient } from "../../lib/auth-client";

            const form = document.querySelector<HTMLFormElement>("#signup-form");
            
            form?.addEventListener("submit", async (event: SubmitEvent) => {
                event.preventDefault();
                const formData = new FormData(form);
                
                // サインアップ処理
                const data = await authClient.signUp.email({
                    email: formData.get("email") as string,
                    name: formData.get("name") as string,
                    username: formData.get("username") as string,
                    password: formData.get("password") as string,
                });

                if (data.error) {
                    const errorMessageElement = document.querySelector<HTMLSpanElement>("[data-id='error-message']");
                    errorMessageElement!.textContent = data.error.message ?? "";
                } else {
                    window.location.href = "/";
                }
            });
        </script>
    </body>
</html>

実装時に感じたメリット

データベース設定が簡単

僕自身SQL や SQLite の深い知識はないんですが、特別なことをしない限り Better Auth 側でセットアップを自動的に処理してくれるのがとても良かったです。

公式のドキュメントも充実しており、迷うことなくデータベースの設定ができました。

強力なプラグインシステム

今回はシンプルにメールアドレスやパスワードなどといった最低限の情報でユーザー登録やログイン機能を作ったんですが、Better Auth ではプラグインを追加していく形でいろんなログイン方法に対応できるのが大きいと感じました。

例えば、パスキーを追加するには

  1. auth.ts に passkey のインポートをし plugin として passkey を追加
  2. データベースをマイグレート
  3. auth-client.ts にも同様に passkey をインポートした上で plugin として passkeyClient()を追加する

たったこれだけで設定が完了します。あとはページ側で passkey でのログイン処理を作成するだけです。簡単ですよね。

しかも、Better Auth ではパスキーだけじゃなく、二要素認証やユーザーネームでのログイン、マジックリンク、OTP によるログイン、SSO や OIDC による外部サービスのログインなどいろんなログイン方法に対応しているところが魅力的です。

フレームワークに依存しない設計

Better Auth を使っていて特に驚いたのは、フレームワークに依存しない点です。

Auth.js では基本的に Next.js にしか対応しておらず、Qwik や SvelteKit などは試験的な実装しかされていませんでした。

ところが、Better Auth ではフレームワークに依存しない作りになっているため、フロントエンド側はバニラJS だけでも動作させることができます。

そのため、さまざまな構成にも対応できるところがやはり強みだと感じました。

おわりに

ということで今回は Better Auth を使って Astro で簡単な認証機能を実装してみました。

Auth.js から Better Auth への移行が進んでいる今、新しいプロジェクトであれば Better Auth を選択するのが “Better” だと思います。(ここ笑いどころです)

設定はシンプルだし、drizzle-orm などの複雑な設定なしに認証機能を実装できる点が魅力的です。
(もちろん ORM(Object-Relational Mapping)にも対応可能です。)

今回の実装では、わずか数行の設定でサインアップ・サインイン・ログアウト機能が完成しました。

さらにはプラグインシステムにより、必要に応じて機能を拡張することも簡単です。
Better Auth には先ほど触れた通り OAuth プロバイダー、二要素認証、パスワードリセットなど、より高度な認証機能が豊富に用意されています。

ぜひ、この機会に Better Auth を試してみて、その便利さを体験してみてください!

この記事が役に立ったと思ったら、
ぜひ「いいね」とシェアをお願いします!

リンクをコピーXでシェアするfacebookでシェアする

この記事を書いた人

柴田 俊也
柴田 俊也
2020年にメンバーズキャリア(現:メンバーズ)に新卒入社。デザインコーダーとして3年ほど経験し、フロントエンドエンジニアに転向。現在は企業の常駐型内製化支援の業務を行なっています。
詳しく見る
ページトップへ戻る