Skip to content

Commit 26a3d36

Browse files
committed
better-auth+drizzle+pg
1 parent 508a93f commit 26a3d36

File tree

11 files changed

+534
-30
lines changed

11 files changed

+534
-30
lines changed

README.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,30 @@
33
https://my-code.utcode.net
44

55
## インストール
6+
67
```bash
78
npm ci
89
```
910

11+
## 開発環境
12+
13+
```bash
14+
npx prisma dev
15+
```
16+
を実行し、`t` キーを押して表示される DATABASE_URL をコピー
17+
1018
ルートディレクトリに .env.local という名前のファイルを作成し、以下の内容を記述
1119
```dotenv
1220
API_KEY=GeminiAPIキー
1321
BETTER_AUTH_URL=http://localhost:3000
22+
DATABASE_URL="postgres://... (prisma devの出力)"
1423
```
1524

16-
prismaの開発環境を起動
17-
(.env にDATABASE_URLが自動的に追加される)
25+
別のターミナルで、
1826
```bash
19-
npx prisma dev
20-
```
21-
別ターミナルで
22-
```bash
23-
npx prisma db push
27+
npx drizzle-kit migrate
2428
```
25-
26-
### 本番環境の場合
27-
28-
上記の環境変数以外に、
29-
* BETTER_AUTH_SECRET に任意の文字列
30-
* DATABASE_URL に本番用のPostgreSQLデータベースURL
31-
* GOOGLE_CLIENT_IDとGOOGLE_CLIENT_SECRETにGoogle OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/google
32-
* GITHUB_CLIENT_IDとGITHUB_CLIENT_SECRETにGitHub OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/github
33-
34-
35-
## 開発環境
29+
でデータベースを初期化
3630

3731
```bash
3832
npm run dev
@@ -49,6 +43,13 @@ npm run lint
4943
```
5044
でコードをチェックします。出てくるwarningやerrorはできるだけ直しましょう。
5145

46+
### 本番環境の場合
47+
48+
上記の環境変数以外に、
49+
* BETTER_AUTH_SECRET に任意の文字列
50+
* GOOGLE_CLIENT_IDとGOOGLE_CLIENT_SECRETにGoogle OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/google
51+
* GITHUB_CLIENT_IDとGITHUB_CLIENT_SECRETにGitHub OAuthのクライアントIDとシークレット https://www.better-auth.com/docs/authentication/github
52+
5253
## markdown仕様
5354

5455
````

app/api/auth/[...all]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { getAuthServer } from "@/lib/auth";
2-
import { getPrismaClient } from "@/lib/prisma";
2+
import { getDrizzle } from "@/lib/drizzle";
33
import { toNextJsHandler } from "better-auth/next-js";
44

55
export const { POST, GET } = toNextJsHandler({
66
handler: async (req) =>
7-
(await getAuthServer(await getPrismaClient())).handler(req),
7+
(await getAuthServer(await getDrizzle())).handler(req),
88
});

app/lib/auth.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { betterAuth } from "better-auth";
2-
import { prismaAdapter } from "better-auth/adapters/prisma";
2+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
33
import { getCloudflareContext } from "@opennextjs/cloudflare";
44
import { anonymous } from "better-auth/plugins";
55
import { migrateChatUser } from "./chatHistory";
6-
import { PrismaClient } from "@prisma/client";
6+
import { getDrizzle } from "./drizzle";
77

8-
export async function getAuthServer(prisma: PrismaClient) {
8+
export async function getAuthServer(drizzle: ReturnType<typeof getDrizzle>) {
99
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1010
let cloudflareEnv: any;
1111
try {
@@ -15,8 +15,8 @@ export async function getAuthServer(prisma: PrismaClient) {
1515
cloudflareEnv = {};
1616
}
1717
return betterAuth({
18-
database: prismaAdapter(prisma, {
19-
provider: "sqlite",
18+
database: drizzleAdapter(drizzle, {
19+
provider: "pg",
2020
}),
2121
plugins: [
2222
anonymous({
@@ -42,3 +42,6 @@ export async function getAuthServer(prisma: PrismaClient) {
4242
},
4343
});
4444
}
45+
46+
// @better-auth/cli を実行するときだけ以下のコメントアウトを解除
47+
// export const auth = await getAuthServer(await getDrizzle());

app/lib/chatHistory.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { headers } from "next/headers";
22
import { getAuthServer } from "./auth";
3-
import { getPrismaClient } from "./prisma";
43

54
export interface CreateChatMessage {
65
role: "user" | "ai" | "error";

app/lib/drizzle.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { getCloudflareContext } from "@opennextjs/cloudflare";
2+
import { drizzle } from "drizzle-orm/node-postgres";
3+
import { Pool } from "pg";
4+
import * as authSchema from "../schema/auth";
5+
6+
export async function getDrizzle() {
7+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8+
let cloudflareEnv: any;
9+
try {
10+
cloudflareEnv = (await getCloudflareContext({ async: true })).env;
11+
} catch {
12+
// @better-auth/cli generate を実行する際には initOpenNextCloudflareForDev がセットアップされていない環境になっている
13+
cloudflareEnv = {};
14+
}
15+
const DATABASE_URL =
16+
process.env.DATABASE_URL ?? cloudflareEnv.DATABASE_URL ?? "";
17+
18+
const pool = new Pool({
19+
connectionString: DATABASE_URL,
20+
// You don't want to reuse the same connection for multiple requests
21+
maxUses: 1,
22+
});
23+
return drizzle({
24+
client: pool,
25+
schema: {
26+
...authSchema,
27+
},
28+
});
29+
}

app/schema/auth.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
2+
3+
export const user = pgTable("user", {
4+
id: text("id").primaryKey(),
5+
name: text("name").notNull(),
6+
email: text("email").notNull().unique(),
7+
emailVerified: boolean("email_verified").default(false).notNull(),
8+
image: text("image"),
9+
createdAt: timestamp("created_at").defaultNow().notNull(),
10+
updatedAt: timestamp("updated_at")
11+
.defaultNow()
12+
.$onUpdate(() => /* @__PURE__ */ new Date())
13+
.notNull(),
14+
isAnonymous: boolean("is_anonymous"),
15+
});
16+
17+
export const session = pgTable("session", {
18+
id: text("id").primaryKey(),
19+
expiresAt: timestamp("expires_at").notNull(),
20+
token: text("token").notNull().unique(),
21+
createdAt: timestamp("created_at").defaultNow().notNull(),
22+
updatedAt: timestamp("updated_at")
23+
.$onUpdate(() => /* @__PURE__ */ new Date())
24+
.notNull(),
25+
ipAddress: text("ip_address"),
26+
userAgent: text("user_agent"),
27+
userId: text("user_id")
28+
.notNull()
29+
.references(() => user.id, { onDelete: "cascade" }),
30+
});
31+
32+
export const account = pgTable("account", {
33+
id: text("id").primaryKey(),
34+
accountId: text("account_id").notNull(),
35+
providerId: text("provider_id").notNull(),
36+
userId: text("user_id")
37+
.notNull()
38+
.references(() => user.id, { onDelete: "cascade" }),
39+
accessToken: text("access_token"),
40+
refreshToken: text("refresh_token"),
41+
idToken: text("id_token"),
42+
accessTokenExpiresAt: timestamp("access_token_expires_at"),
43+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
44+
scope: text("scope"),
45+
password: text("password"),
46+
createdAt: timestamp("created_at").defaultNow().notNull(),
47+
updatedAt: timestamp("updated_at")
48+
.$onUpdate(() => /* @__PURE__ */ new Date())
49+
.notNull(),
50+
});
51+
52+
export const verification = pgTable("verification", {
53+
id: text("id").primaryKey(),
54+
identifier: text("identifier").notNull(),
55+
value: text("value").notNull(),
56+
expiresAt: timestamp("expires_at").notNull(),
57+
createdAt: timestamp("created_at").defaultNow().notNull(),
58+
updatedAt: timestamp("updated_at")
59+
.defaultNow()
60+
.$onUpdate(() => /* @__PURE__ */ new Date())
61+
.notNull(),
62+
});

drizzle.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import "dotenv/config";
2+
import { defineConfig } from "drizzle-kit";
3+
4+
export default defineConfig({
5+
dialect: "postgresql", // 'mysql' | 'sqlite' | 'turso'
6+
schema: "./app/schema",
7+
dbCredentials: {
8+
url: process.env.DATABASE_URL || "",
9+
},
10+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
CREATE TABLE "account" (
2+
"id" text PRIMARY KEY NOT NULL,
3+
"account_id" text NOT NULL,
4+
"provider_id" text NOT NULL,
5+
"user_id" text NOT NULL,
6+
"access_token" text,
7+
"refresh_token" text,
8+
"id_token" text,
9+
"access_token_expires_at" timestamp,
10+
"refresh_token_expires_at" timestamp,
11+
"scope" text,
12+
"password" text,
13+
"created_at" timestamp DEFAULT now() NOT NULL,
14+
"updated_at" timestamp NOT NULL
15+
);
16+
--> statement-breakpoint
17+
CREATE TABLE "session" (
18+
"id" text PRIMARY KEY NOT NULL,
19+
"expires_at" timestamp NOT NULL,
20+
"token" text NOT NULL,
21+
"created_at" timestamp DEFAULT now() NOT NULL,
22+
"updated_at" timestamp NOT NULL,
23+
"ip_address" text,
24+
"user_agent" text,
25+
"user_id" text NOT NULL,
26+
CONSTRAINT "session_token_unique" UNIQUE("token")
27+
);
28+
--> statement-breakpoint
29+
CREATE TABLE "user" (
30+
"id" text PRIMARY KEY NOT NULL,
31+
"name" text NOT NULL,
32+
"email" text NOT NULL,
33+
"email_verified" boolean DEFAULT false NOT NULL,
34+
"image" text,
35+
"created_at" timestamp DEFAULT now() NOT NULL,
36+
"updated_at" timestamp DEFAULT now() NOT NULL,
37+
"is_anonymous" boolean,
38+
CONSTRAINT "user_email_unique" UNIQUE("email")
39+
);
40+
--> statement-breakpoint
41+
CREATE TABLE "verification" (
42+
"id" text PRIMARY KEY NOT NULL,
43+
"identifier" text NOT NULL,
44+
"value" text NOT NULL,
45+
"expires_at" timestamp NOT NULL,
46+
"created_at" timestamp DEFAULT now() NOT NULL,
47+
"updated_at" timestamp DEFAULT now() NOT NULL
48+
);
49+
--> statement-breakpoint
50+
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
51+
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;

0 commit comments

Comments
 (0)