@auth/drizzle-adapter 0.9.0 → 1.0.1

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
@@ -1,264 +1,447 @@
1
- import { and, eq } from "drizzle-orm"
1
+ import { and, eq, getTableColumns } from "drizzle-orm"
2
2
  import {
3
+ MySqlColumn,
4
+ MySqlDatabase,
5
+ MySqlTableWithColumns,
6
+ PreparedQueryHKTBase,
7
+ QueryResultHKT,
3
8
  int,
4
- timestamp,
5
- mysqlTable as defaultMySqlTableFn,
9
+ mysqlTable,
6
10
  primaryKey,
11
+ timestamp,
7
12
  varchar,
8
- MySqlTableFn,
9
- MySqlDatabase,
10
13
  } from "drizzle-orm/mysql-core"
11
14
 
12
- import type { Adapter, AdapterAccount } from "@auth/core/adapters"
15
+ import type {
16
+ Adapter,
17
+ AdapterAccount,
18
+ AdapterSession,
19
+ AdapterUser,
20
+ VerificationToken,
21
+ } from "@auth/core/adapters"
13
22
 
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
- })
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
- )
23
+ export const mysqlUsersTable = mysqlTable("user", {
24
+ id: varchar("id", { length: 255 })
25
+ .primaryKey()
26
+ .$defaultFn(() => crypto.randomUUID()),
27
+ name: varchar("name", { length: 255 }),
28
+ email: varchar("email", { length: 255 }).notNull(),
29
+ emailVerified: timestamp("emailVerified", { mode: "date", fsp: 3 }),
30
+ image: varchar("image", { length: 255 }),
31
+ }) satisfies DefaultMySqlUsersTable
51
32
 
52
- const sessions = mySqlTable("session", {
53
- sessionToken: varchar("sessionToken", { length: 255 })
54
- .notNull()
55
- .primaryKey(),
33
+ export const mysqlAccountsTable = mysqlTable(
34
+ "account",
35
+ {
56
36
  userId: varchar("userId", { length: 255 })
57
37
  .notNull()
58
- .references(() => users.id, { onDelete: "cascade" }),
59
- expires: timestamp("expires", { mode: "date" }).notNull(),
38
+ .references(() => mysqlUsersTable.id, { onDelete: "cascade" }),
39
+ type: varchar("type", { length: 255 })
40
+ .$type<AdapterAccount["type"]>()
41
+ .notNull(),
42
+ provider: varchar("provider", { length: 255 }).notNull(),
43
+ providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
44
+ refresh_token: varchar("refresh_token", { length: 255 }),
45
+ access_token: varchar("access_token", { length: 255 }),
46
+ expires_at: int("expires_at"),
47
+ token_type: varchar("token_type", { length: 255 }),
48
+ scope: varchar("scope", { length: 255 }),
49
+ id_token: varchar("id_token", { length: 2048 }),
50
+ session_state: varchar("session_state", { length: 255 }),
51
+ },
52
+ (account) => ({
53
+ compositePk: primaryKey({
54
+ columns: [account.provider, account.providerAccountId],
55
+ }),
60
56
  })
57
+ ) satisfies DefaultMySqlAccountsTable
61
58
 
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
- }
59
+ export const mysqlSessionsTable = mysqlTable("session", {
60
+ sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(),
61
+ userId: varchar("userId", { length: 255 })
62
+ .notNull()
63
+ .references(() => mysqlUsersTable.id, { onDelete: "cascade" }),
64
+ expires: timestamp("expires", { mode: "date" }).notNull(),
65
+ }) satisfies DefaultMySqlSessionsTable
76
66
 
77
- export type DefaultSchema = ReturnType<typeof createTables>
67
+ export const mysqlVerificationTokensTable = mysqlTable(
68
+ "verificationToken",
69
+ {
70
+ identifier: varchar("identifier", { length: 255 }).notNull(),
71
+ token: varchar("token", { length: 255 }).notNull(),
72
+ expires: timestamp("expires", { mode: "date" }).notNull(),
73
+ },
74
+ (vt) => ({
75
+ compositePk: primaryKey({ columns: [vt.identifier, vt.token] }),
76
+ })
77
+ ) satisfies DefaultMySqlVerificationTokenTable
78
78
 
79
- export function mySqlDrizzleAdapter(
80
- client: InstanceType<typeof MySqlDatabase>,
81
- tableFn = defaultMySqlTableFn
79
+ export function MySqlDrizzleAdapter(
80
+ client: MySqlDatabase<QueryResultHKT, PreparedQueryHKTBase, any>,
81
+ schema: DefaultMySqlSchema = {
82
+ usersTable: mysqlUsersTable,
83
+ accountsTable: mysqlAccountsTable,
84
+ sessionsTable: mysqlSessionsTable,
85
+ verificationTokensTable: mysqlVerificationTokensTable,
86
+ }
82
87
  ): Adapter {
83
- const { users, accounts, sessions, verificationTokens } =
84
- createTables(tableFn)
88
+ const { usersTable, accountsTable, sessionsTable, verificationTokensTable } =
89
+ schema
85
90
 
86
91
  return {
87
- async createUser(data) {
88
- const id = crypto.randomUUID()
92
+ async createUser(data: AdapterUser) {
93
+ const hasDefaultId = getTableColumns(usersTable)["id"]["hasDefault"]
89
94
 
90
- await client.insert(users).values({ ...data, id })
95
+ await client
96
+ .insert(usersTable)
97
+ .values(hasDefaultId ? data : { ...data, id: crypto.randomUUID() })
91
98
 
92
- return await client
99
+ return client
93
100
  .select()
94
- .from(users)
95
- .where(eq(users.id, id))
101
+ .from(usersTable)
102
+ .where(eq(usersTable.email, data.email))
96
103
  .then((res) => res[0])
97
104
  },
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
105
+ async getUser(userId: string) {
106
+ return client
107
+ .select()
108
+ .from(usersTable)
109
+ .where(eq(usersTable.id, userId))
110
+ .then((res) => (res.length > 0 ? res[0] : null))
107
111
  },
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
112
+ async getUserByEmail(email: string) {
113
+ return client
114
+ .select()
115
+ .from(usersTable)
116
+ .where(eq(usersTable.email, email))
117
+ .then((res) => (res.length > 0 ? res[0] : null))
117
118
  },
118
- async createSession(data) {
119
- await client.insert(sessions).values(data)
119
+ async createSession(data: {
120
+ sessionToken: string
121
+ userId: string
122
+ expires: Date
123
+ }) {
124
+ await client.insert(sessionsTable).values(data)
120
125
 
121
- return await client
126
+ return client
122
127
  .select()
123
- .from(sessions)
124
- .where(eq(sessions.sessionToken, data.sessionToken))
128
+ .from(sessionsTable)
129
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
125
130
  .then((res) => res[0])
126
131
  },
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
132
+ async getSessionAndUser(sessionToken: string) {
133
+ return client
134
+ .select({
135
+ session: sessionsTable,
136
+ user: usersTable,
137
+ })
138
+ .from(sessionsTable)
139
+ .where(eq(sessionsTable.sessionToken, sessionToken))
140
+ .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))
141
+ .then((res) => (res.length > 0 ? res[0] : null))
140
142
  },
141
- async updateUser(data) {
143
+ async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, "id">) {
142
144
  if (!data.id) {
143
145
  throw new Error("No user id.")
144
146
  }
145
147
 
146
- await client.update(users).set(data).where(eq(users.id, data.id))
148
+ await client
149
+ .update(usersTable)
150
+ .set(data)
151
+ .where(eq(usersTable.id, data.id))
147
152
 
148
- return await client
153
+ const [result] = await client
149
154
  .select()
150
- .from(users)
151
- .where(eq(users.id, data.id))
152
- .then((res) => res[0])
155
+ .from(usersTable)
156
+ .where(eq(usersTable.id, data.id))
157
+
158
+ if (!result) {
159
+ throw new Error("No user found.")
160
+ }
161
+
162
+ return result
153
163
  },
154
- async updateSession(data) {
164
+ async updateSession(
165
+ data: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
166
+ ) {
155
167
  await client
156
- .update(sessions)
168
+ .update(sessionsTable)
157
169
  .set(data)
158
- .where(eq(sessions.sessionToken, data.sessionToken))
170
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
159
171
 
160
- return await client
172
+ return client
161
173
  .select()
162
- .from(sessions)
163
- .where(eq(sessions.sessionToken, data.sessionToken))
174
+ .from(sessionsTable)
175
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
164
176
  .then((res) => res[0])
165
177
  },
166
- async linkAccount(rawAccount) {
167
- await client.insert(accounts).values(rawAccount)
178
+ async linkAccount(data: AdapterAccount) {
179
+ await client.insert(accountsTable).values(data)
168
180
  },
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
- )
181
+ async getUserByAccount(
182
+ account: Pick<AdapterAccount, "provider" | "providerAccountId">
183
+ ) {
184
+ const result = await client
185
+ .select({
186
+ account: accountsTable,
187
+ user: usersTable,
188
+ })
189
+ .from(accountsTable)
190
+ .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))
191
+ .where(
192
+ and(
193
+ eq(accountsTable.provider, account.provider),
194
+ eq(accountsTable.providerAccountId, account.providerAccountId)
179
195
  )
180
- .leftJoin(users, eq(accounts.userId, users.id))
181
- .then((res) => res[0])) ?? null
182
-
183
- if (!dbAccount) {
184
- return null
185
- }
196
+ )
197
+ .then((res) => res[0])
186
198
 
187
- return dbAccount.user
199
+ return result?.user ?? null
188
200
  },
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
-
201
+ async deleteSession(sessionToken: string) {
197
202
  await client
198
- .delete(sessions)
199
- .where(eq(sessions.sessionToken, sessionToken))
200
-
201
- return session
203
+ .delete(sessionsTable)
204
+ .where(eq(sessionsTable.sessionToken, sessionToken))
202
205
  },
203
- async createVerificationToken(token) {
204
- await client.insert(verificationTokens).values(token)
206
+ async createVerificationToken(data: VerificationToken) {
207
+ await client.insert(verificationTokensTable).values(data)
205
208
 
206
- return await client
209
+ return client
207
210
  .select()
208
- .from(verificationTokens)
209
- .where(eq(verificationTokens.identifier, token.identifier))
211
+ .from(verificationTokensTable)
212
+ .where(eq(verificationTokensTable.identifier, data.identifier))
210
213
  .then((res) => res[0])
211
214
  },
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
215
+ async useVerificationToken(params: { identifier: string; token: string }) {
216
+ const deletedToken = await client
217
+ .select()
218
+ .from(verificationTokensTable)
219
+ .where(
220
+ and(
221
+ eq(verificationTokensTable.identifier, params.identifier),
222
+ eq(verificationTokensTable.token, params.token)
223
+ )
224
+ )
225
+ .then((res) => (res.length > 0 ? res[0] : null))
225
226
 
227
+ if (deletedToken) {
226
228
  await client
227
- .delete(verificationTokens)
229
+ .delete(verificationTokensTable)
228
230
  .where(
229
231
  and(
230
- eq(verificationTokens.identifier, token.identifier),
231
- eq(verificationTokens.token, token.token)
232
+ eq(verificationTokensTable.identifier, params.identifier),
233
+ eq(verificationTokensTable.token, params.token)
232
234
  )
233
235
  )
234
-
235
- return deletedToken
236
- } catch (err) {
237
- throw new Error("No verification token found.")
238
236
  }
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
-
247
- await client.delete(users).where(eq(users.id, id))
248
237
 
249
- return user
238
+ return deletedToken
250
239
  },
251
- async unlinkAccount(account) {
240
+ async deleteUser(id: string) {
241
+ await client.delete(usersTable).where(eq(usersTable.id, id))
242
+ },
243
+ async unlinkAccount(
244
+ params: Pick<AdapterAccount, "provider" | "providerAccountId">
245
+ ) {
252
246
  await client
253
- .delete(accounts)
247
+ .delete(accountsTable)
254
248
  .where(
255
249
  and(
256
- eq(accounts.providerAccountId, account.providerAccountId),
257
- eq(accounts.provider, account.provider)
250
+ eq(accountsTable.provider, params.provider),
251
+ eq(accountsTable.providerAccountId, params.providerAccountId)
258
252
  )
259
253
  )
260
-
261
- return undefined
262
254
  },
263
255
  }
264
256
  }
257
+
258
+ type DefaultMyqlColumn<
259
+ T extends {
260
+ data: string | number | Date
261
+ dataType: "string" | "number" | "date"
262
+ notNull: boolean
263
+ columnType: "MySqlVarChar" | "MySqlText" | "MySqlTimestamp" | "MySqlInt"
264
+ },
265
+ > = MySqlColumn<{
266
+ name: string
267
+ columnType: T["columnType"]
268
+ data: T["data"]
269
+ driverParam: string | number
270
+ notNull: T["notNull"]
271
+ hasDefault: boolean
272
+ enumValues: any
273
+ dataType: T["dataType"]
274
+ tableName: string
275
+ }>
276
+
277
+ export type DefaultMySqlUsersTable = MySqlTableWithColumns<{
278
+ name: string
279
+ columns: {
280
+ id: DefaultMyqlColumn<{
281
+ data: string
282
+ dataType: "string"
283
+ notNull: true
284
+ columnType: "MySqlVarChar" | "MySqlText"
285
+ }>
286
+ name: DefaultMyqlColumn<{
287
+ data: string
288
+ dataType: "string"
289
+ notNull: boolean
290
+ columnType: "MySqlVarChar" | "MySqlText"
291
+ }>
292
+ email: DefaultMyqlColumn<{
293
+ data: string
294
+ dataType: "string"
295
+ notNull: true
296
+ columnType: "MySqlVarChar" | "MySqlText"
297
+ }>
298
+ emailVerified: DefaultMyqlColumn<{
299
+ data: Date
300
+ dataType: "date"
301
+ notNull: boolean
302
+ columnType: "MySqlTimestamp"
303
+ }>
304
+ image: DefaultMyqlColumn<{
305
+ data: string
306
+ dataType: "string"
307
+ notNull: boolean
308
+ columnType: "MySqlVarChar" | "MySqlText"
309
+ }>
310
+ }
311
+ dialect: "mysql"
312
+ schema: string | undefined
313
+ }>
314
+
315
+ export type DefaultMySqlAccountsTable = MySqlTableWithColumns<{
316
+ name: string
317
+ columns: {
318
+ userId: DefaultMyqlColumn<{
319
+ columnType: "MySqlVarChar" | "MySqlText"
320
+ data: string
321
+ notNull: true
322
+ dataType: "string"
323
+ }>
324
+ type: DefaultMyqlColumn<{
325
+ columnType: "MySqlVarChar" | "MySqlText"
326
+ data: string
327
+ notNull: true
328
+ dataType: "string"
329
+ }>
330
+ provider: DefaultMyqlColumn<{
331
+ columnType: "MySqlVarChar" | "MySqlText"
332
+ data: string
333
+ notNull: true
334
+ dataType: "string"
335
+ }>
336
+ providerAccountId: DefaultMyqlColumn<{
337
+ dataType: "string"
338
+ columnType: "MySqlVarChar" | "MySqlText"
339
+ data: string
340
+ notNull: true
341
+ }>
342
+ refresh_token: DefaultMyqlColumn<{
343
+ dataType: "string"
344
+ columnType: "MySqlVarChar" | "MySqlText"
345
+ data: string
346
+ notNull: boolean
347
+ }>
348
+ access_token: DefaultMyqlColumn<{
349
+ dataType: "string"
350
+ columnType: "MySqlVarChar" | "MySqlText"
351
+ data: string
352
+ driverParam: string | number
353
+ notNull: boolean
354
+ }>
355
+ expires_at: DefaultMyqlColumn<{
356
+ dataType: "number"
357
+ columnType: "MySqlInt"
358
+ data: number
359
+ notNull: boolean
360
+ }>
361
+ token_type: DefaultMyqlColumn<{
362
+ dataType: "string"
363
+ columnType: "MySqlVarChar" | "MySqlText"
364
+ data: string
365
+ notNull: boolean
366
+ }>
367
+ scope: DefaultMyqlColumn<{
368
+ dataType: "string"
369
+ columnType: "MySqlVarChar" | "MySqlText"
370
+ data: string
371
+ notNull: boolean
372
+ }>
373
+ id_token: DefaultMyqlColumn<{
374
+ dataType: "string"
375
+ columnType: "MySqlVarChar" | "MySqlText"
376
+ data: string
377
+ notNull: boolean
378
+ }>
379
+ session_state: DefaultMyqlColumn<{
380
+ dataType: "string"
381
+ columnType: "MySqlVarChar" | "MySqlText"
382
+ data: string
383
+ notNull: boolean
384
+ }>
385
+ }
386
+ dialect: "mysql"
387
+ schema: string | undefined
388
+ }>
389
+
390
+ export type DefaultMySqlSessionsTable = MySqlTableWithColumns<{
391
+ name: string
392
+ columns: {
393
+ sessionToken: DefaultMyqlColumn<{
394
+ columnType: "MySqlVarChar" | "MySqlText"
395
+ data: string
396
+ notNull: true
397
+ dataType: "string"
398
+ }>
399
+ userId: DefaultMyqlColumn<{
400
+ columnType: "MySqlVarChar" | "MySqlText"
401
+ data: string
402
+ notNull: true
403
+ dataType: "string"
404
+ }>
405
+ expires: DefaultMyqlColumn<{
406
+ dataType: "date"
407
+ columnType: "MySqlTimestamp"
408
+ data: Date
409
+ notNull: true
410
+ }>
411
+ }
412
+ dialect: "mysql"
413
+ schema: string | undefined
414
+ }>
415
+
416
+ export type DefaultMySqlVerificationTokenTable = MySqlTableWithColumns<{
417
+ name: string
418
+ columns: {
419
+ identifier: DefaultMyqlColumn<{
420
+ columnType: "MySqlVarChar" | "MySqlText"
421
+ data: string
422
+ notNull: true
423
+ dataType: "string"
424
+ }>
425
+ token: DefaultMyqlColumn<{
426
+ columnType: "MySqlVarChar" | "MySqlText"
427
+ data: string
428
+ notNull: true
429
+ dataType: "string"
430
+ }>
431
+ expires: DefaultMyqlColumn<{
432
+ dataType: "date"
433
+ columnType: "MySqlTimestamp"
434
+ data: Date
435
+ notNull: true
436
+ }>
437
+ }
438
+ dialect: "mysql"
439
+ schema: string | undefined
440
+ }>
441
+
442
+ export type DefaultMySqlSchema = {
443
+ usersTable: DefaultMySqlUsersTable
444
+ accountsTable: DefaultMySqlAccountsTable
445
+ sessionsTable: DefaultMySqlSessionsTable
446
+ verificationTokensTable: DefaultMySqlVerificationTokenTable
447
+ }