0
# Database Integration
1
2
Adapter interface for integrating with any database backend, supporting user management, session storage, and account linking. Enables persistent sessions and user data across application restarts.
3
4
## Capabilities
5
6
### Adapter Interface
7
8
Main interface for database adapters that handle user and session persistence.
9
10
```typescript { .api }
11
/**
12
* Database adapter interface for NextAuth.js persistence
13
*/
14
interface Adapter {
15
/** Create a new user in the database */
16
createUser?: (user: Omit<AdapterUser, "id">) => Awaitable<AdapterUser>;
17
/** Get user by unique identifier */
18
getUser?: (id: string) => Awaitable<AdapterUser | null>;
19
/** Get user by email address */
20
getUserByEmail?: (email: string) => Awaitable<AdapterUser | null>;
21
/** Get user by provider account information */
22
getUserByAccount?: (
23
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
24
) => Awaitable<AdapterUser | null>;
25
/** Update existing user information */
26
updateUser?: (
27
user: Partial<AdapterUser> & Pick<AdapterUser, "id">
28
) => Awaitable<AdapterUser>;
29
/** Delete user from database (future implementation) */
30
deleteUser?: (
31
userId: string
32
) => Promise<void> | Awaitable<AdapterUser | null | undefined>;
33
/** Link provider account to user */
34
linkAccount?: (
35
account: AdapterAccount
36
) => Promise<void> | Awaitable<AdapterAccount | null | undefined>;
37
/** Unlink provider account from user (future implementation) */
38
unlinkAccount?: (
39
providerAccountId: Pick<AdapterAccount, "provider" | "providerAccountId">
40
) => Promise<void> | Awaitable<AdapterAccount | undefined>;
41
/** Create session for authenticated user */
42
createSession?: (session: {
43
sessionToken: string;
44
userId: string;
45
expires: Date;
46
}) => Awaitable<AdapterSession>;
47
/** Get session and associated user data */
48
getSessionAndUser?: (
49
sessionToken: string
50
) => Awaitable<{ session: AdapterSession; user: AdapterUser } | null>;
51
/** Update session expiration or properties */
52
updateSession?: (
53
session: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
54
) => Awaitable<AdapterSession | null | undefined>;
55
/** Delete session from database */
56
deleteSession?: (
57
sessionToken: string
58
) => Promise<void> | Awaitable<AdapterSession | null | undefined>;
59
/** Create email verification token */
60
createVerificationToken?: (
61
verificationToken: VerificationToken
62
) => Awaitable<VerificationToken | null | undefined>;
63
/** Use and delete verification token */
64
useVerificationToken?: (params: {
65
identifier: string;
66
token: string;
67
}) => Awaitable<VerificationToken | null>;
68
}
69
```
70
71
### User Management
72
73
Adapter methods for managing user accounts and profile information.
74
75
```typescript { .api }
76
/**
77
* User data structure for database storage
78
*/
79
interface AdapterUser extends User {
80
id: string;
81
email: string;
82
emailVerified: Date | null;
83
}
84
85
interface User extends DefaultUser {
86
id: string;
87
name?: string | null;
88
email?: string | null;
89
image?: string | null;
90
}
91
92
interface DefaultUser {
93
id: string;
94
name?: string | null;
95
email?: string | null;
96
image?: string | null;
97
}
98
```
99
100
**Usage Examples:**
101
102
```typescript
103
// Prisma adapter implementation example
104
import { PrismaAdapter } from "@next-auth/prisma-adapter";
105
import { PrismaClient } from "@prisma/client";
106
107
const prisma = new PrismaClient();
108
109
export default NextAuth({
110
adapter: PrismaAdapter(prisma),
111
providers: [...],
112
});
113
114
// Custom adapter implementation
115
const customAdapter: Adapter = {
116
async createUser(user) {
117
const newUser = await db.user.create({
118
data: {
119
name: user.name,
120
email: user.email,
121
image: user.image,
122
emailVerified: user.emailVerified,
123
},
124
});
125
return newUser;
126
},
127
128
async getUser(id) {
129
const user = await db.user.findUnique({ where: { id } });
130
return user;
131
},
132
133
async getUserByEmail(email) {
134
const user = await db.user.findUnique({ where: { email } });
135
return user;
136
},
137
138
async updateUser(user) {
139
const updatedUser = await db.user.update({
140
where: { id: user.id },
141
data: user,
142
});
143
return updatedUser;
144
},
145
146
// Implement other required methods...
147
};
148
149
export default NextAuth({
150
adapter: customAdapter,
151
providers: [...],
152
});
153
```
154
155
### Account Linking
156
157
Methods for managing provider account associations with users.
158
159
```typescript { .api }
160
/**
161
* Provider account data structure
162
*/
163
interface AdapterAccount extends Account {
164
userId: string;
165
}
166
167
interface Account extends Partial<TokenSetParameters> {
168
providerAccountId: string;
169
userId?: string;
170
provider: string;
171
type: ProviderType;
172
access_token?: string;
173
expires_at?: number;
174
id_token?: string;
175
refresh_token?: string;
176
scope?: string;
177
session_state?: string;
178
token_type?: string;
179
}
180
```
181
182
**Usage Examples:**
183
184
```typescript
185
// Account linking implementation
186
const adapter: Adapter = {
187
async linkAccount(account) {
188
await db.account.create({
189
data: {
190
userId: account.userId,
191
provider: account.provider,
192
providerAccountId: account.providerAccountId,
193
type: account.type,
194
access_token: account.access_token,
195
expires_at: account.expires_at,
196
id_token: account.id_token,
197
refresh_token: account.refresh_token,
198
scope: account.scope,
199
session_state: account.session_state,
200
token_type: account.token_type,
201
},
202
});
203
},
204
205
async getUserByAccount({ provider, providerAccountId }) {
206
const account = await db.account.findUnique({
207
where: {
208
provider_providerAccountId: {
209
provider,
210
providerAccountId,
211
},
212
},
213
include: { user: true },
214
});
215
216
return account?.user ?? null;
217
},
218
219
async unlinkAccount({ provider, providerAccountId }) {
220
await db.account.delete({
221
where: {
222
provider_providerAccountId: {
223
provider,
224
providerAccountId,
225
},
226
},
227
});
228
},
229
};
230
```
231
232
### Session Management
233
234
Database session storage for persistent authentication state.
235
236
```typescript { .api }
237
/**
238
* Database session data structure
239
*/
240
interface AdapterSession {
241
/** Randomly generated session token */
242
sessionToken: string;
243
/** User ID associated with session */
244
userId: string;
245
/** Session expiration date */
246
expires: Date;
247
}
248
```
249
250
**Usage Examples:**
251
252
```typescript
253
// Session management implementation
254
const adapter: Adapter = {
255
async createSession({ sessionToken, userId, expires }) {
256
const session = await db.session.create({
257
data: {
258
sessionToken,
259
userId,
260
expires,
261
},
262
});
263
return session;
264
},
265
266
async getSessionAndUser(sessionToken) {
267
const sessionAndUser = await db.session.findUnique({
268
where: { sessionToken },
269
include: { user: true },
270
});
271
272
if (!sessionAndUser) return null;
273
274
const { user, ...session } = sessionAndUser;
275
return { user, session };
276
},
277
278
async updateSession({ sessionToken, ...session }) {
279
const updatedSession = await db.session.update({
280
where: { sessionToken },
281
data: session,
282
});
283
return updatedSession;
284
},
285
286
async deleteSession(sessionToken) {
287
const session = await db.session.delete({
288
where: { sessionToken },
289
});
290
return session;
291
},
292
};
293
294
// Configure NextAuth to use database sessions
295
export default NextAuth({
296
adapter,
297
session: {
298
strategy: "database",
299
maxAge: 30 * 24 * 60 * 60, // 30 days
300
updateAge: 24 * 60 * 60, // 24 hours
301
},
302
providers: [...],
303
});
304
```
305
306
### Email Verification
307
308
Token-based email verification system for passwordless authentication.
309
310
```typescript { .api }
311
/**
312
* Email verification token structure
313
*/
314
interface VerificationToken {
315
/** Email address or identifier */
316
identifier: string;
317
/** Token expiration date */
318
expires: Date;
319
/** Verification token string */
320
token: string;
321
}
322
```
323
324
**Usage Examples:**
325
326
```typescript
327
// Email verification implementation
328
const adapter: Adapter = {
329
async createVerificationToken({ identifier, expires, token }) {
330
const verificationToken = await db.verificationToken.create({
331
data: {
332
identifier,
333
expires,
334
token,
335
},
336
});
337
return verificationToken;
338
},
339
340
async useVerificationToken({ identifier, token }) {
341
try {
342
const verificationToken = await db.verificationToken.delete({
343
where: {
344
identifier_token: {
345
identifier,
346
token,
347
},
348
},
349
});
350
return verificationToken;
351
} catch (error) {
352
// Token not found or already used
353
return null;
354
}
355
},
356
};
357
358
// Email provider with verification
359
import EmailProvider from "next-auth/providers/email";
360
361
export default NextAuth({
362
adapter,
363
providers: [
364
EmailProvider({
365
server: process.env.EMAIL_SERVER,
366
from: process.env.EMAIL_FROM,
367
}),
368
],
369
});
370
```
371
372
### Popular Database Adapters
373
374
NextAuth.js provides official adapters for popular databases and ORMs.
375
376
```typescript { .api }
377
/**
378
* Popular database adapters available
379
*/
380
interface DatabaseAdapters {
381
/** Prisma ORM adapter */
382
PrismaAdapter: (prisma: PrismaClient) => Adapter;
383
/** MongoDB adapter */
384
MongoDBAdapter: (client: MongoClient, options?: MongoDBAdapterOptions) => Adapter;
385
/** DynamoDB adapter */
386
DynamoDBAdapter: (client: DynamoDBDocument, options?: DynamoDBAdapterOptions) => Adapter;
387
/** Firebase Firestore adapter */
388
FirestoreAdapter: (firestore: Firestore) => Adapter;
389
/** Supabase adapter */
390
SupabaseAdapter: (client: SupabaseClient, options?: SupabaseAdapterOptions) => Adapter;
391
/** TypeORM adapter */
392
TypeORMAdapter: (connection: Connection | DataSource, options?: TypeORMAdapterOptions) => Adapter;
393
/** Sequelize adapter */
394
SequelizeAdapter: (sequelize: Sequelize) => Adapter;
395
}
396
```
397
398
**Usage Examples:**
399
400
```typescript
401
// Prisma adapter
402
import { PrismaAdapter } from "@next-auth/prisma-adapter";
403
import { PrismaClient } from "@prisma/client";
404
405
const prisma = new PrismaClient();
406
407
export default NextAuth({
408
adapter: PrismaAdapter(prisma),
409
providers: [...],
410
});
411
412
// MongoDB adapter
413
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
414
import { MongoClient } from "mongodb";
415
416
const client = new MongoClient(process.env.MONGODB_URI!);
417
const clientPromise = client.connect();
418
419
export default NextAuth({
420
adapter: MongoDBAdapter(clientPromise),
421
providers: [...],
422
});
423
424
// DynamoDB adapter
425
import { DynamoDBAdapter } from "@next-auth/dynamodb-adapter";
426
import { DynamoDB } from "@aws-sdk/client-dynamodb";
427
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
428
429
const client = DynamoDBDocument.from(new DynamoDB({}), {
430
marshallOptions: {
431
convertEmptyValues: true,
432
removeUndefinedValues: true,
433
convertClassInstanceToMap: true,
434
},
435
});
436
437
export default NextAuth({
438
adapter: DynamoDBAdapter(client),
439
providers: [...],
440
});
441
442
// Supabase adapter
443
import { SupabaseAdapter } from "@next-auth/supabase-adapter";
444
import { createClient } from "@supabase/supabase-js";
445
446
const supabase = createClient(
447
process.env.NEXT_PUBLIC_SUPABASE_URL!,
448
process.env.SUPABASE_SERVICE_ROLE_KEY!
449
);
450
451
export default NextAuth({
452
adapter: SupabaseAdapter({
453
url: process.env.NEXT_PUBLIC_SUPABASE_URL!,
454
secret: process.env.SUPABASE_SERVICE_ROLE_KEY!,
455
}),
456
providers: [...],
457
});
458
```
459
460
### Custom Adapter Implementation
461
462
Guidelines and patterns for implementing custom database adapters.
463
464
```typescript { .api }
465
/**
466
* Custom adapter implementation utilities
467
*/
468
interface CustomAdapterPatterns {
469
/** Implement required methods first */
470
requiredMethods: (keyof Adapter)[];
471
/** Optional methods for enhanced functionality */
472
optionalMethods: (keyof Adapter)[];
473
/** Error handling patterns */
474
errorHandling: {
475
handleNotFound: () => null;
476
handleDuplicateKey: (error: any) => never;
477
handleConnectionError: (error: any) => never;
478
};
479
}
480
```
481
482
**Usage Examples:**
483
484
```typescript
485
// Custom MySQL adapter example
486
import mysql from 'mysql2/promise';
487
488
function MySQLAdapter(connection: mysql.Connection): Adapter {
489
return {
490
async createUser(user) {
491
const [result] = await connection.execute(
492
'INSERT INTO users (name, email, image, emailVerified) VALUES (?, ?, ?, ?)',
493
[user.name, user.email, user.image, user.emailVerified]
494
);
495
496
const [rows] = await connection.execute(
497
'SELECT * FROM users WHERE id = ?',
498
[(result as any).insertId]
499
);
500
501
return (rows as any)[0];
502
},
503
504
async getUser(id) {
505
const [rows] = await connection.execute(
506
'SELECT * FROM users WHERE id = ?',
507
[id]
508
);
509
510
return (rows as any)[0] || null;
511
},
512
513
async getUserByEmail(email) {
514
const [rows] = await connection.execute(
515
'SELECT * FROM users WHERE email = ?',
516
[email]
517
);
518
519
return (rows as any)[0] || null;
520
},
521
522
async getUserByAccount({ provider, providerAccountId }) {
523
const [rows] = await connection.execute(`
524
SELECT u.* FROM users u
525
JOIN accounts a ON u.id = a.userId
526
WHERE a.provider = ? AND a.providerAccountId = ?
527
`, [provider, providerAccountId]);
528
529
return (rows as any)[0] || null;
530
},
531
532
async updateUser(user) {
533
await connection.execute(
534
'UPDATE users SET name = ?, email = ?, image = ?, emailVerified = ? WHERE id = ?',
535
[user.name, user.email, user.image, user.emailVerified, user.id]
536
);
537
538
const [rows] = await connection.execute(
539
'SELECT * FROM users WHERE id = ?',
540
[user.id]
541
);
542
543
return (rows as any)[0];
544
},
545
546
async linkAccount(account) {
547
await connection.execute(`
548
INSERT INTO accounts (
549
userId, provider, providerAccountId, type,
550
access_token, expires_at, id_token, refresh_token,
551
scope, session_state, token_type
552
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
553
`, [
554
account.userId, account.provider, account.providerAccountId, account.type,
555
account.access_token, account.expires_at, account.id_token, account.refresh_token,
556
account.scope, account.session_state, account.token_type
557
]);
558
},
559
560
async createSession({ sessionToken, userId, expires }) {
561
await connection.execute(
562
'INSERT INTO sessions (sessionToken, userId, expires) VALUES (?, ?, ?)',
563
[sessionToken, userId, expires]
564
);
565
566
return { sessionToken, userId, expires };
567
},
568
569
async getSessionAndUser(sessionToken) {
570
const [rows] = await connection.execute(`
571
SELECT s.*, u.* FROM sessions s
572
JOIN users u ON s.userId = u.id
573
WHERE s.sessionToken = ?
574
`, [sessionToken]);
575
576
const row = (rows as any)[0];
577
if (!row) return null;
578
579
return {
580
session: {
581
sessionToken: row.sessionToken,
582
userId: row.userId,
583
expires: row.expires
584
},
585
user: {
586
id: row.id,
587
name: row.name,
588
email: row.email,
589
image: row.image,
590
emailVerified: row.emailVerified
591
}
592
};
593
},
594
595
async updateSession({ sessionToken, ...session }) {
596
await connection.execute(
597
'UPDATE sessions SET expires = ? WHERE sessionToken = ?',
598
[session.expires, sessionToken]
599
);
600
601
const [rows] = await connection.execute(
602
'SELECT * FROM sessions WHERE sessionToken = ?',
603
[sessionToken]
604
);
605
606
return (rows as any)[0];
607
},
608
609
async deleteSession(sessionToken) {
610
const [rows] = await connection.execute(
611
'SELECT * FROM sessions WHERE sessionToken = ?',
612
[sessionToken]
613
);
614
615
await connection.execute(
616
'DELETE FROM sessions WHERE sessionToken = ?',
617
[sessionToken]
618
);
619
620
return (rows as any)[0] || null;
621
},
622
623
async createVerificationToken({ identifier, expires, token }) {
624
await connection.execute(
625
'INSERT INTO verification_tokens (identifier, expires, token) VALUES (?, ?, ?)',
626
[identifier, expires, token]
627
);
628
629
return { identifier, expires, token };
630
},
631
632
async useVerificationToken({ identifier, token }) {
633
const [rows] = await connection.execute(
634
'SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?',
635
[identifier, token]
636
);
637
638
if ((rows as any).length === 0) return null;
639
640
await connection.execute(
641
'DELETE FROM verification_tokens WHERE identifier = ? AND token = ?',
642
[identifier, token]
643
);
644
645
return (rows as any)[0];
646
},
647
};
648
}
649
650
export default NextAuth({
651
adapter: MySQLAdapter(connection),
652
providers: [...],
653
});
654
```
655
656
### Database Schema Requirements
657
658
Required database schema structure for NextAuth.js adapters.
659
660
```typescript { .api }
661
/**
662
* Required database tables and columns
663
*/
664
interface DatabaseSchema {
665
users: {
666
id: string; // Primary key
667
name?: string | null;
668
email?: string | null; // Unique
669
emailVerified?: Date | null;
670
image?: string | null;
671
};
672
673
accounts: {
674
id?: string; // Primary key (optional)
675
userId: string; // Foreign key to users.id
676
provider: string;
677
providerAccountId: string;
678
type: string;
679
access_token?: string;
680
expires_at?: number;
681
id_token?: string;
682
refresh_token?: string;
683
scope?: string;
684
session_state?: string;
685
token_type?: string;
686
// Compound unique key on (provider, providerAccountId)
687
};
688
689
sessions: {
690
id?: string; // Primary key (optional)
691
sessionToken: string; // Unique
692
userId: string; // Foreign key to users.id
693
expires: Date;
694
};
695
696
verification_tokens: {
697
identifier: string;
698
token: string;
699
expires: Date;
700
// Compound unique key on (identifier, token)
701
};
702
}
703
```
704
705
## Types
706
707
### Adapter Data Types
708
709
```typescript { .api }
710
type Awaitable<T> = T | PromiseLike<T>;
711
712
interface TokenSetParameters {
713
access_token?: string;
714
token_type?: string;
715
id_token?: string;
716
refresh_token?: string;
717
scope?: string;
718
expires_at?: number;
719
session_state?: string;
720
}
721
722
type ProviderType = "oauth" | "email" | "credentials";
723
```
724
725
### Database Connection Types
726
727
```typescript { .api }
728
interface DatabaseConnectionOptions {
729
host: string;
730
port: number;
731
database: string;
732
username: string;
733
password: string;
734
ssl?: boolean;
735
connectionLimit?: number;
736
acquireTimeout?: number;
737
timeout?: number;
738
}
739
740
interface ConnectionPool {
741
getConnection: () => Promise<any>;
742
releaseConnection: (connection: any) => void;
743
end: () => Promise<void>;
744
}
745
```