ファランクスブログ

© 2026 all rights reserved.
  1. blog
  2. better-auth
  • PrismaAdapter
  • セッションクッキー
  • SignIn / Out / Session
  • サーバー
  • クライアント
  • ユーザー登録
  • callbackURL
  • databaseHooks
  • 複数OAuthと同一Email
  • Tanstack

Nextjs/Tanstack:Better-Auth

2026年4月6日

https://www.better-auth.com/docs/introduction

PrismaAdapter

https://www.prisma.io/docs/guides/betterauth-nextjs

pnpm add better-auth
npm install prisma tsx @types/pg --save-dev
npm install @prisma/client @prisma/adapter-pg dotenv pg

auth.ts:

export const auth = betterAuth({
  plugins: [nextCookies()],
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 60 * 60 * 24 * 7,
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.AUTH_GOOGLE_ID as string,
      clientSecret: process.env.AUTH_GOOGLE_SECRET as string,
    },
  },
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  trustedOrigins: [env.BASE_URL as string],
  databaseHooks: {}
});
  • sessionの部分を明示的に書いているが、デフォルト値は上記と同じ

  • databaseHooks:の中にはUserがcreateやupdateされた時のロジックを書ける

schema.prisma:https://www.prisma.io/docs/guides/betterauth-nextjs

  • Schemaファイルが空ならpnpm dlx @better-auth/cli@latest generateで生成

  • 空ではない場合はDocsからAuth関連のモデルをペースト

セッションクッキー

export const AUTH_SESSION_COOKIE = {
  prod: "__Secure-better-auth.session_token",
  dev: "better-auth.session_token",
};
  • 上記はデフォルトのセッションcookie名

  • productionだと __Secureがcookie名の接頭辞につくので、開発時と本番ではcookie名が異なるので三項演算などが必要になる。これはcookie名を変えても同様。

    • __Secureはhttpsじゃないとcookieを保存しないというブラウザ側の仕組み

// auth.ts

  advanced: {
    cookies: {
      session_token: {
        name: "my_app_session_token",
        attributes: {
          httpOnly: true,
          secure: process.env.NODE_ENV === "production",
          sameSite: "lax",
        },
      },
    },
  },
  • 名前を変える場合

SignIn / Out / Session

サーバー

サーバー側の場合 haeders()を使うので、 "use server"を書いたファイルから関数をexportする

SignIn:

const Page = () => {
  return (
    <form action={signInServer}>
      <button type="submit">Sign in with Google</button>
    </form>
  );
};
"use server";
import { redirect } from "next/navigation";
import { auth } from "../lib/auth";

export async function signInServer() {
  const res = await auth.api.signInSocial({
    body: {
      provider: "google",
    },
  });
  if (res.url) {
    redirect(res.url);
  }
}
  • *auth.tsに plugins: [nextCookies()]を追加しないとGoogleのURLに遷移せずにエラーになる。

SignOut:

"use server";
import { headers } from "next/headers";
import { auth } from "../auth/auth";

export async function signOutServer() {
  await auth.api.signOut({
    headers: await headers(),
  });
}

Session:

  const data = await auth.api.getSession({
    headers: await headers(),
  });

---

export const getServerSession = async () => {
  return await auth.api.getSession({
    headers: await headers(), 
  });
};

クライアント

lib/auth-client.ts :

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

export const { signIn, signUp, signOut, useSession } = createAuthClient();

SignIn / Out:

const GoogleSignIn = () => {
  const pathname = usePathname();
  return (
    <button
      type="submit"
      className="cursor-pointer"
      onClick={() =>
        signIn.social({
          provider: "google",
          callbackURL: pathname,
        })
      }
    >
      <FcGoogle size={20} />
    </button>
  );
};

export default GoogleSignIn;

export const GoogleSignOut = () => {
  return (
    <Button
        onClick={() => {
          signOut();
          // window.location.reload();
          router.replace("/test");
        }}
      type="submit"
      variant={"link"}
      className="cursor-pointer bg-red-500 text-white"
    >
      Sign out
    </Button>
  );
};
  • signInはcallbackURLが無いと"/"にリダイレクトされるので、ログインしたルートにredirectさせるにはusePathname等で現在のパスを取得する。

Session:

  const { data, error } = useSession();

ユーザー登録

PrismaAdapterがSignIn時に自動で作る User を使うか、SignIn時に作られる User を元に独自の App_User を作成

  • *基本的にはPrismaAdapterで作成されたUserを拡充するだけで充分。

  • UserやSessionには型が用意されている。

import { Session, User } from "better-auth";

独自ユーザーの作成例

callbackURL

最初にやっていた方法だが、callbackURLは認証が終わった後にユーザーをどこに戻すかが目的なので、User登録に用いるのは間違った使い方と思われる。

SignInボタン:

const GoogleSignIn = () => {
  return (
    <button
      type="submit"
      onClick={() =>
        signIn.social({
          provider: "google",
          callbackURL: "/api/auth/user",
        })
      }
    >
      <FcGoogle size={20} />
    </button>
  );
};
  • callbackURLはGoogleサインインが正常に行われた後にブラウザが遷移する先

route.ts:

export async function GET() {
  const session = await getServerSession();

  if (!session) {
    return NextResponse.redirect(`${BASE_URL}/sign-in`);
  }

  const { email, name, image } = session.user;

  // ユーザー登録処理

  // リダイレクト 
    return NextResponse.redirect(`${BASE_URL}/`);
  }
}
  • Sessionの所に書いた使い回し可能なSession取得関数を用いてSession情報をもとに独自のUserテーブルを作成

  • *returnするのはNextResponse.jsonではなくNextResponse.redirect にする。NextResponse.json だとAPIルートがブラウザに表示されたままになる

databaseHooks

auth.ts:

export const auth = betterAuth({
  ...

  databaseHooks: {
    user: {
      create: {
        after: async (user) => {
          if (!user?.email) return;

          const existingUser = await prisma.app_user.findUnique({
            where: { email: user.email },
          });
          if (existingUser) return;

          await prisma.app_user.create({
            data: {
              email: user.email,
              name: user.name,
              image: user.image,
            },
          });
        },
      },
    },
  },
});
  • databaseHooks内にSignInしたユーザーが登録している/していない場合の処理を書く

  • SignInボタンのcallbackURLはリダイレクトさせたいルートにする

複数OAuthと同一Email

例えばGoogleでSignInをしてUserが作成された後に、Googleと同じEmailを使っているGihubでSignInしたときには新規Userではなく、既存のUserに紐付けたい。

auth.ts:

import { prisma } from "@/prisma/prisma";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";

export const auth = betterAuth({
  secret: process.env.BETTER_AUTH_SECRET,
  baseURL: process.env.BETTER_AUTH_URL,
  database: prismaAdapter(prisma, { provider: "postgresql" }),
  socialProviders: {
    google: {
      clientId: process.env.AUTH_GOOGLE_ID as string,
      clientSecret: process.env.AUTH_GOOGLE_SECRET as string,
    },
    github: {
      clientId: process.env.AUTH_GITHUB_ID as string,
      clientSecret: process.env.AUTH_GITHUB_SECRET as string,
    },
  },
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google", "github"],
    },
  },

export default auth;
  • accountLinkingの部分を追加する。

  • 動作としては、accountテーブルはOAuth毎に重複なしで作成されていくが、UserテーブルはEmailが同じなら既存のUserに紐付けられる。

Tanstack

auth.ts:

import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start";
import { env } from "cloudflare:workers";

export const auth = betterAuth({
  database: env.test_db,
  socialProviders: {
    google: {
      clientId: process.env.AUTH_GOOGLE_ID,
      clientSecret: process.env.AUTH_GOOGLE_SECRET,
    },
  },
  plugins: [tanstackStartCookies()],
  baseURL: process.env.BETTER_AUTH_URL,
});
  • PrismaAdapterを使うことができないが、databaseにD1のDBを書くとSignIn時にUserがテーブルに作成されるので使用感はNextjs x Prisma統合と変わらない。

nextjs
/
prisma
auth
tanstack
  • PrismaAdapter
  • セッションクッキー
  • SignIn / Out / Session
  • サーバー
  • クライアント
  • ユーザー登録
  • callbackURL
  • databaseHooks
  • 複数OAuthと同一Email
  • Tanstack