Skip to content

Commit 0223e1f

Browse files
fix(mongodb-adapter): prevent MongoDB client promise being cached (#11393)
Co-authored-by: Nico Domino <[email protected]>
1 parent 4b59e20 commit 0223e1f

File tree

5 files changed

+36
-38
lines changed

5 files changed

+36
-38
lines changed

docs/pages/getting-started/adapters/mongodb.mdx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ MONGODB_URI=
3030
```ts filename="./auth.ts"
3131
import NextAuth from "next-auth"
3232
import { MongoDBAdapter } from "@auth/mongodb-adapter"
33-
import clientPromise from "./lib/db"
33+
import client from "./lib/db"
3434

3535
export const { handlers, auth, signIn, signOut } = NextAuth({
36-
adapter: MongoDBAdapter(clientPromise),
36+
adapter: MongoDBAdapter(client),
3737
providers: [],
3838
})
3939
```
@@ -44,12 +44,12 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
4444
```ts filename="/src/routes/[email protected]"
4545
import { QwikAuth$ } from "@auth/qwik"
4646
import { MongoDBAdapter } from "@auth/mongodb-adapter"
47-
import clientPromise from "./lib/db"
47+
import client from "./lib/db"
4848

4949
export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
5050
() => ({
5151
providers: [],
52-
adapter: MongoDBAdapter(clientPromise),
52+
adapter: MongoDBAdapter(client),
5353
})
5454
)
5555
```
@@ -60,10 +60,10 @@ export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
6060
```ts filename="./src/auth.ts"
6161
import { SvelteKitAuth } from "@auth/sveltekit"
6262
import { MongoDBAdapter } from "@auth/mongodb-adapter"
63-
import clientPromise from "./lib/db"
63+
import client from "./lib/db"
6464

6565
export const { handle, signIn, signOut } = SvelteKitAuth({
66-
adapter: MongoDBAdapter(clientPromise),
66+
adapter: MongoDBAdapter(client),
6767
})
6868
```
6969

@@ -73,7 +73,7 @@ export const { handle, signIn, signOut } = SvelteKitAuth({
7373
```ts filename="./src/routes/auth.route.ts"
7474
import { ExpressAuth } from "@auth/express"
7575
import { MongoDBAdapter } from "@auth/mongodb-adapter"
76-
import clientPromise from "./lib/db"
76+
import client from "./lib/db"
7777

7878
const app = express()
7979

@@ -82,7 +82,7 @@ app.use(
8282
"/auth/*",
8383
ExpressAuth({
8484
providers: [],
85-
adapter: MongoDBAdapter(clientPromise),
85+
adapter: MongoDBAdapter(client),
8686
})
8787
)
8888
```
@@ -111,28 +111,25 @@ const options = {
111111
},
112112
}
113113

114-
let client
115-
let clientPromise: Promise<MongoClient>
114+
let client: MongoClient
116115

117116
if (process.env.NODE_ENV === "development") {
118117
// In development mode, use a global variable so that the value
119118
// is preserved across module reloads caused by HMR (Hot Module Replacement).
120119
let globalWithMongo = global as typeof globalThis & {
121-
_mongoClientPromise?: Promise<MongoClient>
120+
_mongoClient?: MongoClient
122121
}
123122

124-
if (!globalWithMongo._mongoClientPromise) {
125-
client = new MongoClient(uri, options)
126-
globalWithMongo._mongoClientPromise = client.connect()
123+
if (!globalWithMongo._mongoClient) {
124+
globalWithMongo._mongoClient = new MongoClient(uri, options)
127125
}
128-
clientPromise = globalWithMongo._mongoClientPromise
126+
client = globalWithMongo._mongoClient
129127
} else {
130128
// In production mode, it's best to not use a global variable.
131129
client = new MongoClient(uri, options)
132-
clientPromise = client.connect()
133130
}
134131

135-
// Export a module-scoped MongoClient promise. By doing this in a
132+
// Export a module-scoped MongoClient. By doing this in a
136133
// separate module, the client can be shared across functions.
137-
export default clientPromise
134+
export default client
138135
```

packages/adapter-mongodb/src/index.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,8 @@ export interface MongoDBAdapterOptions {
5252
databaseName?: string
5353
/**
5454
* Callback function for managing the closing of the MongoDB client.
55-
* This could be useful in serverless environments, especially when `client`
56-
* is provided as a function returning Promise<MongoClient>, not just a simple promise.
57-
* It allows for more sophisticated management of database connections,
55+
* This could be useful when `client` is provided as a function returning MongoClient | Promise<MongoClient>.
56+
* It allows for more customized management of database connections,
5857
* addressing persistence, container reuse, and connection closure issues.
5958
*/
6059
onClose?: (client: MongoClient) => Promise<void>
@@ -108,18 +107,29 @@ export function _id(hex?: string) {
108107

109108
export function MongoDBAdapter(
110109
/**
111-
* The MongoDB client. You can either pass a promise that resolves to a `MongoClient` or a function that returns a promise that resolves to a `MongoClient`.
112-
* Using a function that returns a `Promise<MongoClient>` could be useful in serverless environments, particularly when combined with `options.onClose`, to efficiently handle database connections and address challenges with persistence, container reuse, and connection closure.
113-
* These functions enable either straightforward open-close database connections or more complex caching and connection reuse strategies.
110+
* The MongoDB client.
111+
*
112+
* The MongoDB team recommends providing a non-connected `MongoClient` instance to avoid unhandled promise rejections if the client fails to connect.
113+
*
114+
* Alternatively, you can also pass:
115+
* - A promise that resolves to a connected `MongoClient` (not recommended).
116+
* - A function, to handle more complex and custom connection strategies.
117+
*
118+
* Using a function that returns `MongoClient | Promise<MongoClient>`, combined with `options.onClose`, can be useful when you want a more advanced and customized connection strategy to address challenges related to persistence, container reuse, and connection closure.
114119
*/
115-
client: Promise<MongoClient> | (() => Promise<MongoClient>),
120+
client:
121+
| MongoClient
122+
| Promise<MongoClient>
123+
| (() => MongoClient | Promise<MongoClient>),
116124
options: MongoDBAdapterOptions = {}
117125
): Adapter {
118126
const { collections } = options
119127
const { from, to } = format
120128

121129
const getDb = async () => {
122-
const _client = await (typeof client === "function" ? client() : client)
130+
const _client: MongoClient = await (typeof client === "function"
131+
? client()
132+
: client)
123133
const _db = _client.db(options.databaseName)
124134
const c = { ...defaultCollections, ...collections }
125135
return {

packages/adapter-mongodb/test/custom.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { defaultCollections, format, MongoDBAdapter, _id } from "../src"
33
import { MongoClient } from "mongodb"
44
const name = "custom-test"
55
const client = new MongoClient(`mongodb://localhost:27017/${name}`)
6-
const clientPromise = client.connect()
76

87
const collections = { ...defaultCollections, Users: "some_userz" }
98

109
runBasicTests({
11-
adapter: MongoDBAdapter(clientPromise, {
10+
adapter: MongoDBAdapter(client, {
1211
collections,
1312
}),
1413
db: {

packages/adapter-mongodb/test/index.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import { MongoClient } from "mongodb"
44

55
const name = "test"
66
const client = new MongoClient(`mongodb://localhost:27017/${name}`)
7-
const clientPromise = client.connect()
87

98
runBasicTests({
10-
adapter: MongoDBAdapter(clientPromise),
9+
adapter: MongoDBAdapter(client),
1110
db: {
1211
async disconnect() {
1312
await client.db().dropDatabase()

packages/adapter-mongodb/test/serverless.test.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import { MongoClient } from "mongodb"
44
import { expect, test, vi } from "vitest"
55

66
const name = "serverless-test"
7-
const clientPromise = new MongoClient(
8-
`mongodb://localhost:27017/${name}`
9-
).connect()
7+
const client = new MongoClient(`mongodb://localhost:27017/${name}`)
108

119
const onClose = vi.fn(async (client: MongoClient) => {
1210
await client.close()
@@ -29,12 +27,10 @@ runBasicTests({
2927
),
3028
db: {
3129
async disconnect() {
32-
const client = await clientPromise
3330
await client.db().dropDatabase()
3431
await client.close()
3532
},
3633
async user(id) {
37-
const client = await clientPromise
3834
const user = await client
3935
.db()
4036
.collection(defaultCollections.Users)
@@ -44,7 +40,6 @@ runBasicTests({
4440
return format.from(user)
4541
},
4642
async account(provider_providerAccountId) {
47-
const client = await clientPromise
4843
const account = await client
4944
.db()
5045
.collection(defaultCollections.Accounts)
@@ -53,7 +48,6 @@ runBasicTests({
5348
return format.from(account)
5449
},
5550
async session(sessionToken) {
56-
const client = await clientPromise
5751
const session = await client
5852
.db()
5953
.collection(defaultCollections.Sessions)
@@ -62,7 +56,6 @@ runBasicTests({
6256
return format.from(session)
6357
},
6458
async verificationToken(identifier_token) {
65-
const client = await clientPromise
6659
const token = await client
6760
.db()
6861
.collection(defaultCollections.VerificationTokens)

0 commit comments

Comments
 (0)