@auth/drizzle-adapter 0.9.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/lib/mysql.ts CHANGED
@@ -2,263 +2,282 @@ import { and, eq } from "drizzle-orm"
2
2
  import {
3
3
  int,
4
4
  timestamp,
5
- mysqlTable as defaultMySqlTableFn,
6
5
  primaryKey,
7
6
  varchar,
8
- MySqlTableFn,
9
7
  MySqlDatabase,
8
+ mysqlTable,
9
+ MySqlTableWithColumns,
10
+ TableConfig,
11
+ QueryResultHKT,
12
+ PreparedQueryHKTBase,
13
+ index,
10
14
  } from "drizzle-orm/mysql-core"
11
15
 
12
- import type { Adapter, AdapterAccount } from "@auth/core/adapters"
16
+ import type {
17
+ Adapter,
18
+ AdapterUser,
19
+ AdapterAccount,
20
+ AdapterSession,
21
+ VerificationToken,
22
+ } from "@auth/core/adapters"
13
23
 
14
- export function createTables(mySqlTable: MySqlTableFn) {
15
- const users = mySqlTable("user", {
16
- id: varchar("id", { length: 255 }).notNull().primaryKey(),
17
- name: varchar("name", { length: 255 }),
18
- email: varchar("email", { length: 255 }).notNull(),
19
- emailVerified: timestamp("emailVerified", {
20
- mode: "date",
21
- fsp: 3,
22
- }).defaultNow(),
23
- image: varchar("image", { length: 255 }),
24
- })
24
+ import { randomUUID } from "crypto"
25
25
 
26
- const accounts = mySqlTable(
27
- "account",
28
- {
29
- userId: varchar("userId", { length: 255 })
30
- .notNull()
31
- .references(() => users.id, { onDelete: "cascade" }),
32
- type: varchar("type", { length: 255 })
33
- .$type<AdapterAccount["type"]>()
34
- .notNull(),
35
- provider: varchar("provider", { length: 255 }).notNull(),
36
- providerAccountId: varchar("providerAccountId", {
37
- length: 255,
38
- }).notNull(),
39
- refresh_token: varchar("refresh_token", { length: 255 }),
40
- access_token: varchar("access_token", { length: 255 }),
41
- expires_at: int("expires_at"),
42
- token_type: varchar("token_type", { length: 255 }),
43
- scope: varchar("scope", { length: 255 }),
44
- id_token: varchar("id_token", { length: 255 }),
45
- session_state: varchar("session_state", { length: 255 }),
46
- },
47
- (account) => ({
48
- compoundKey: primaryKey(account.provider, account.providerAccountId),
49
- })
50
- )
26
+ export const mysqlUsersTable = mysqlTable("user" as string, {
27
+ id: varchar("id", { length: 255 })
28
+ .primaryKey()
29
+ .$defaultFn(() => randomUUID()),
30
+ name: varchar("name", { length: 255 }),
31
+ email: varchar("email", { length: 255 }).notNull().unique(),
32
+ emailVerified: timestamp("emailVerified", { mode: "date", fsp: 3 }),
33
+ image: varchar("image", { length: 255 }),
34
+ })
51
35
 
52
- const sessions = mySqlTable("session", {
53
- sessionToken: varchar("sessionToken", { length: 255 })
36
+ export const mysqlAccountsTable = mysqlTable(
37
+ "account" as string,
38
+ {
39
+ userId: varchar("userId", { length: 255 })
54
40
  .notNull()
55
- .primaryKey(),
41
+ .references(() => mysqlUsersTable.id, { onDelete: "cascade" }),
42
+ type: varchar("type", { length: 255 }).notNull(),
43
+ provider: varchar("provider", { length: 255 }).notNull(),
44
+ providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
45
+ refresh_token: varchar("refresh_token", { length: 255 }),
46
+ access_token: varchar("access_token", { length: 255 }),
47
+ expires_at: int("expires_at"),
48
+ token_type: varchar("token_type", { length: 255 }),
49
+ scope: varchar("scope", { length: 255 }),
50
+ id_token: varchar("id_token", { length: 2048 }),
51
+ session_state: varchar("session_state", { length: 255 }),
52
+ },
53
+ (account) => ({
54
+ compositePk: primaryKey({
55
+ columns: [account.provider, account.providerAccountId],
56
+ }),
57
+ userIdIdx: index("Account_userId_index").on(account.userId),
58
+ })
59
+ )
60
+
61
+ export const mysqlSessionsTable = mysqlTable(
62
+ "session" as string,
63
+ {
64
+ id: varchar("id", { length: 255 })
65
+ .primaryKey()
66
+ .$defaultFn(() => randomUUID()),
67
+ sessionToken: varchar("sessionToken", { length: 255 }).notNull().unique(),
56
68
  userId: varchar("userId", { length: 255 })
57
69
  .notNull()
58
- .references(() => users.id, { onDelete: "cascade" }),
70
+ .references(() => mysqlUsersTable.id, { onDelete: "cascade" }),
59
71
  expires: timestamp("expires", { mode: "date" }).notNull(),
72
+ },
73
+ (session) => ({
74
+ userIdIdx: index("Session_userId_index").on(session.userId),
60
75
  })
76
+ )
61
77
 
62
- const verificationTokens = mySqlTable(
63
- "verificationToken",
64
- {
65
- identifier: varchar("identifier", { length: 255 }).notNull(),
66
- token: varchar("token", { length: 255 }).notNull(),
67
- expires: timestamp("expires", { mode: "date" }).notNull(),
68
- },
69
- (vt) => ({
70
- compoundKey: primaryKey(vt.identifier, vt.token),
71
- })
72
- )
73
-
74
- return { users, accounts, sessions, verificationTokens }
75
- }
76
-
77
- export type DefaultSchema = ReturnType<typeof createTables>
78
+ export const mysqlVerificationTokensTable = mysqlTable(
79
+ "verificationToken" as string,
80
+ {
81
+ identifier: varchar("identifier", { length: 255 }).notNull(),
82
+ token: varchar("token", { length: 255 }).notNull().unique(),
83
+ expires: timestamp("expires", { mode: "date" }).notNull(),
84
+ },
85
+ (vt) => ({
86
+ compositePk: primaryKey({ columns: [vt.identifier, vt.token] }),
87
+ })
88
+ )
78
89
 
79
- export function mySqlDrizzleAdapter(
80
- client: InstanceType<typeof MySqlDatabase>,
81
- tableFn = defaultMySqlTableFn
90
+ export function MySqlDrizzleAdapter(
91
+ client: MySqlDatabase<QueryResultHKT, PreparedQueryHKTBase, any>,
92
+ schema: DefaultMySqlSchema = {
93
+ usersTable: mysqlUsersTable,
94
+ accountsTable: mysqlAccountsTable,
95
+ sessionsTable: mysqlSessionsTable,
96
+ verificationTokensTable: mysqlVerificationTokensTable,
97
+ }
82
98
  ): Adapter {
83
- const { users, accounts, sessions, verificationTokens } =
84
- createTables(tableFn)
99
+ const { usersTable, accountsTable, sessionsTable, verificationTokensTable } =
100
+ schema
85
101
 
86
102
  return {
87
- async createUser(data) {
88
- const id = crypto.randomUUID()
103
+ async createUser(data: Omit<AdapterUser, "id">) {
104
+ const id = randomUUID()
89
105
 
90
- await client.insert(users).values({ ...data, id })
106
+ await client.insert(usersTable).values({ ...data, id })
91
107
 
92
- return await client
108
+ return client
93
109
  .select()
94
- .from(users)
95
- .where(eq(users.id, id))
110
+ .from(usersTable)
111
+ .where(eq(usersTable.id, id))
96
112
  .then((res) => res[0])
97
113
  },
98
- async getUser(data) {
99
- const thing =
100
- (await client
101
- .select()
102
- .from(users)
103
- .where(eq(users.id, data))
104
- .then((res) => res[0])) ?? null
105
-
106
- return thing
114
+ async getUser(userId: string) {
115
+ return client
116
+ .select()
117
+ .from(usersTable)
118
+ .where(eq(usersTable.id, userId))
119
+ .then((res) => (res.length > 0 ? res[0] : null))
107
120
  },
108
- async getUserByEmail(data) {
109
- const user =
110
- (await client
111
- .select()
112
- .from(users)
113
- .where(eq(users.email, data))
114
- .then((res) => res[0])) ?? null
115
-
116
- return user
121
+ async getUserByEmail(email: string) {
122
+ return client
123
+ .select()
124
+ .from(usersTable)
125
+ .where(eq(usersTable.email, email))
126
+ .then((res) => (res.length > 0 ? res[0] : null))
117
127
  },
118
- async createSession(data) {
119
- await client.insert(sessions).values(data)
128
+ async createSession(data: {
129
+ sessionToken: string
130
+ userId: string
131
+ expires: Date
132
+ }) {
133
+ const id = randomUUID()
120
134
 
121
- return await client
135
+ await client.insert(sessionsTable).values({ ...data, id })
136
+
137
+ return client
122
138
  .select()
123
- .from(sessions)
124
- .where(eq(sessions.sessionToken, data.sessionToken))
139
+ .from(sessionsTable)
140
+ .where(eq(sessionsTable.id, id))
125
141
  .then((res) => res[0])
126
142
  },
127
- async getSessionAndUser(data) {
128
- const sessionAndUser =
129
- (await client
130
- .select({
131
- session: sessions,
132
- user: users,
133
- })
134
- .from(sessions)
135
- .where(eq(sessions.sessionToken, data))
136
- .innerJoin(users, eq(users.id, sessions.userId))
137
- .then((res) => res[0])) ?? null
138
-
139
- return sessionAndUser
143
+ async getSessionAndUser(sessionToken: string) {
144
+ return client
145
+ .select({
146
+ session: sessionsTable,
147
+ user: usersTable,
148
+ })
149
+ .from(sessionsTable)
150
+ .where(eq(sessionsTable.sessionToken, sessionToken))
151
+ .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))
152
+ .then((res) => (res.length > 0 ? res[0] : null))
140
153
  },
141
- async updateUser(data) {
154
+ async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, "id">) {
142
155
  if (!data.id) {
143
156
  throw new Error("No user id.")
144
157
  }
145
158
 
146
- await client.update(users).set(data).where(eq(users.id, data.id))
159
+ await client
160
+ .update(usersTable)
161
+ .set(data)
162
+ .where(eq(usersTable.id, data.id))
147
163
 
148
- return await client
164
+ const [result] = await client
149
165
  .select()
150
- .from(users)
151
- .where(eq(users.id, data.id))
152
- .then((res) => res[0])
166
+ .from(usersTable)
167
+ .where(eq(usersTable.id, data.id))
168
+
169
+ if (!result) {
170
+ throw new Error("No user found.")
171
+ }
172
+
173
+ return result
153
174
  },
154
- async updateSession(data) {
175
+ async updateSession(
176
+ data: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
177
+ ) {
155
178
  await client
156
- .update(sessions)
179
+ .update(sessionsTable)
157
180
  .set(data)
158
- .where(eq(sessions.sessionToken, data.sessionToken))
181
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
159
182
 
160
- return await client
183
+ return client
161
184
  .select()
162
- .from(sessions)
163
- .where(eq(sessions.sessionToken, data.sessionToken))
185
+ .from(sessionsTable)
186
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
164
187
  .then((res) => res[0])
165
188
  },
166
- async linkAccount(rawAccount) {
167
- await client.insert(accounts).values(rawAccount)
189
+ async linkAccount(data: AdapterAccount) {
190
+ await client.insert(accountsTable).values(data)
168
191
  },
169
- async getUserByAccount(account) {
170
- const dbAccount =
171
- (await client
172
- .select()
173
- .from(accounts)
174
- .where(
175
- and(
176
- eq(accounts.providerAccountId, account.providerAccountId),
177
- eq(accounts.provider, account.provider)
178
- )
192
+ async getUserByAccount(
193
+ account: Pick<AdapterAccount, "provider" | "providerAccountId">
194
+ ) {
195
+ const result = await client
196
+ .select({
197
+ account: accountsTable,
198
+ user: usersTable,
199
+ })
200
+ .from(accountsTable)
201
+ .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))
202
+ .where(
203
+ and(
204
+ eq(accountsTable.provider, account.provider),
205
+ eq(accountsTable.providerAccountId, account.providerAccountId)
179
206
  )
180
- .leftJoin(users, eq(accounts.userId, users.id))
181
- .then((res) => res[0])) ?? null
182
-
183
- if (!dbAccount) {
184
- return null
185
- }
207
+ )
208
+ .then((res) => res[0])
186
209
 
187
- return dbAccount.user
210
+ return result?.user ?? null
188
211
  },
189
- async deleteSession(sessionToken) {
190
- const session =
191
- (await client
192
- .select()
193
- .from(sessions)
194
- .where(eq(sessions.sessionToken, sessionToken))
195
- .then((res) => res[0])) ?? null
196
-
212
+ async deleteSession(sessionToken: string) {
197
213
  await client
198
- .delete(sessions)
199
- .where(eq(sessions.sessionToken, sessionToken))
200
-
201
- return session
214
+ .delete(sessionsTable)
215
+ .where(eq(sessionsTable.sessionToken, sessionToken))
202
216
  },
203
- async createVerificationToken(token) {
204
- await client.insert(verificationTokens).values(token)
217
+ async createVerificationToken(data: VerificationToken) {
218
+ await client.insert(verificationTokensTable).values(data)
205
219
 
206
- return await client
220
+ return client
207
221
  .select()
208
- .from(verificationTokens)
209
- .where(eq(verificationTokens.identifier, token.identifier))
222
+ .from(verificationTokensTable)
223
+ .where(eq(verificationTokensTable.identifier, data.identifier))
210
224
  .then((res) => res[0])
211
225
  },
212
- async useVerificationToken(token) {
213
- try {
214
- const deletedToken =
215
- (await client
216
- .select()
217
- .from(verificationTokens)
218
- .where(
219
- and(
220
- eq(verificationTokens.identifier, token.identifier),
221
- eq(verificationTokens.token, token.token)
222
- )
223
- )
224
- .then((res) => res[0])) ?? null
226
+ async useVerificationToken(params: { identifier: string; token: string }) {
227
+ const deletedToken = await client
228
+ .select()
229
+ .from(verificationTokensTable)
230
+ .where(
231
+ and(
232
+ eq(verificationTokensTable.identifier, params.identifier),
233
+ eq(verificationTokensTable.token, params.token)
234
+ )
235
+ )
236
+ .then((res) => (res.length > 0 ? res[0] : null))
225
237
 
238
+ if (deletedToken) {
226
239
  await client
227
- .delete(verificationTokens)
240
+ .delete(verificationTokensTable)
228
241
  .where(
229
242
  and(
230
- eq(verificationTokens.identifier, token.identifier),
231
- eq(verificationTokens.token, token.token)
243
+ eq(verificationTokensTable.identifier, params.identifier),
244
+ eq(verificationTokensTable.token, params.token)
232
245
  )
233
246
  )
234
-
235
- return deletedToken
236
- } catch (err) {
237
- throw new Error("No verification token found.")
238
247
  }
239
- },
240
- async deleteUser(id) {
241
- const user = await client
242
- .select()
243
- .from(users)
244
- .where(eq(users.id, id))
245
- .then((res) => res[0] ?? null)
246
248
 
247
- await client.delete(users).where(eq(users.id, id))
248
-
249
- return user
249
+ return deletedToken
250
+ },
251
+ async deleteUser(id: string) {
252
+ await client.delete(usersTable).where(eq(usersTable.id, id))
250
253
  },
251
- async unlinkAccount(account) {
254
+ async unlinkAccount(
255
+ params: Pick<AdapterAccount, "provider" | "providerAccountId">
256
+ ) {
252
257
  await client
253
- .delete(accounts)
258
+ .delete(accountsTable)
254
259
  .where(
255
260
  and(
256
- eq(accounts.providerAccountId, account.providerAccountId),
257
- eq(accounts.provider, account.provider)
261
+ eq(accountsTable.provider, params.provider),
262
+ eq(accountsTable.providerAccountId, params.providerAccountId)
258
263
  )
259
264
  )
260
-
261
- return undefined
262
265
  },
263
266
  }
264
267
  }
268
+
269
+ export type MySqlTableFn<T extends TableConfig> = MySqlTableWithColumns<{
270
+ name: T["name"]
271
+ columns: T["columns"]
272
+ dialect: T["dialect"]
273
+ schema: string | undefined
274
+ }>
275
+
276
+ export type DefaultMySqlSchema = {
277
+ usersTable: MySqlTableFn<(typeof mysqlUsersTable)["_"]["config"]>
278
+ accountsTable: MySqlTableFn<(typeof mysqlAccountsTable)["_"]["config"]>
279
+ sessionsTable: MySqlTableFn<(typeof mysqlSessionsTable)["_"]["config"]>
280
+ verificationTokensTable: MySqlTableFn<
281
+ (typeof mysqlVerificationTokensTable)["_"]["config"]
282
+ >
283
+ }