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