Open7
Next.js × Drizzle × Neon でGoogle認証を実装

このスクラップでは、Next.js 14アプリケーションにおいて、DrizzleとNeon(PostgreSQL)を使用してGoogle認証を実装する方法をメモっていきます(あくまで個人開発目的)
技術スタック
- Next.js AppRouter
- NextAuth.js
- Drizzle ORM
- Neon (PostgreSQL)
- TypeScript

1. 環境構築
必要なパッケージのインストール
(Next.jsはインストールされている前提)
npm install next-auth @auth/drizzle-adapter drizzle-orm @neondatabase/serverless
npm install -D drizzle-kit
環境変数の設定
.env
に以下の環境変数を設定します。
.env
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
DATABASE_URL=your_neon_database_url
NEXTAUTH_SECRET=your_nextauth_secret
NEXTAUTH_URL=http://localhost:3000 # 本番環境の場合は本番URLを設定
ALLOWED_EMAILS=allowed@example.com,another@example.com
Google Cloud側の設定
以下参考に進めます。

2. データベース接続・スキーマの設定
db/index.ts
にNeonとDrizzleの接続設定を行います。
db/index.ts
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);
schema.ts
にDrizzleのスキーマを定義します。
schema.ts
import { integer, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: text("id")
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
name: text("name"),
email: text("email").notNull(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: text("image"),
});
export const accounts = pgTable("accounts", {
id: serial("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").notNull(),
provider: text("provider").notNull(),
providerAccountId: text("provider_account_id").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
});
export const sessions = pgTable("sessions", {
id: serial("id"),
sessionToken: text("session_token").notNull().primaryKey(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
});
export const verificationTokens = pgTable("verification_tokens", {
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
});

3. 認証設定の実装
auth.ts
で認証の設定を行います。
auth.ts
import NextAuth, { AuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/db";
export const authOptions: AuthOptions = {
adapter: DrizzleAdapter(db),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
async signIn({ user }) {
// 許可されたメールアドレスのみログイン可能(今回のテスト開発に必要だったので実装)
const allowedEmails = process.env.ALLOWED_EMAILS?.split(",") || [];
return allowedEmails.includes(user.email ?? "");
},
// セッションにユーザーIDを追加
async session({ session, user }) {
if (session?.user) {
session.user.id = user.id;
}
return session;
},
},
};

4. APIルートの設定
app/api/auth/[...nextauth]/route.ts
でAPIルートを設定します。
app/api/auth/[...nextauth]/route.ts
import { handler as authHandler } from "@/auth";
export { authHandler as GET, authHandler as POST };

5. データベースのマイグレーション
マイグレーション設定
drizzle.config.ts
を作成します。
drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
package.jsonにscriptsを追加します。
package.json
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
+ "db:generate": "drizzle-kit generate",
+ "db:migrate": "drizzle-kit migrate"
},
マイグレーション実行
マイグレーションファイルを生成します。
npm run db:generate
マイグレーションを実行します。
npm run db:migrate

実際にページ側でどのように認証情報を扱っていくかは別途記載予定。