4

I've recently deployed a small SvelteKit web app to Vercel, using the @sveltejs/adapter-vercel adapter in the svelte.config.js like this:

import adapter from '@sveltejs/adapter-vercel';

const config = {
    kit: {
        adapter: adapter(),
    },
};

As a database, I am using MongoDB (hosted on an Atlas free cluster). The problem is that from time to time (seemingly random), the db cannot be accessed anymore and the following error is thrown:

MongoTopologyClosedError: Topology is closed
    at processWaitQueue (/var/task/node_modules/mongodb/lib/sdam/topology.js:513:46)
    at Topology.selectServer (/var/task/node_modules/mongodb/lib/sdam/topology.js:282:9)
    at Topology.<anonymous> (/var/task/node_modules/mongodb/lib/sdam/topology.js:41:94)
    at node:internal/util:375:7
    at new Promise (<anonymous>)
    at Topology.selectServerAsync (node:internal/util:361:12)
    at executeOperationAsync (/var/task/node_modules/mongodb/lib/operations/execute_operation.js:74:35)
    at /var/task/node_modules/mongodb/lib/operations/execute_operation.js:12:45
    at maybeCallback (/var/task/node_modules/mongodb/lib/utils.js:263:21)
    at executeOperation (/var/task/node_modules/mongodb/lib/operations/execute_operation.js:12:38)

After doing some research, it is not clear to me what exactly causes this error. This is how I connect to the database:

import { MongoClient } from 'mongodb';

const client = new MongoClient(PRIVATE_DB_CONNECTION_STRING);
const db = client.db();

export async function connectToDatabase() {
    try {
        await client.connect();
        console.log('Connected to mongodb');
    } catch (error) {
        console.log(error);
    }
}

export const myCollection = db.collection('randomCollection');

The connectToDatabase() method is called in hooks.server.ts on startup of the application. Does anybody know what could cause this error and how to get rid of it?

gurumaxi
  • 365
  • 2
  • 11

1 Answers1

0

I am also deploying it to Vercel and using this db.ts file that I don't even import in hooks, only in my collection related scripts. It allows the connection to stay alive while reloading in dev mode and to work when deployed.

I adapted this piece of code from the Connect to MongoDB Atlas from SvelteKit blog post.

import { MongoClient } from 'mongodb';

const uri = import.meta.env.VITE_MONGO_URI as string;

const client = new MongoClient(uri);
let clientPromise: Promise<MongoClient>;

if (!uri) {
    throw new Error('Please add your Mongo URI to env variables');
}

declare global {
    // eslint-disable-next-line no-var
    var _mongoClientPromise: Promise<MongoClient>;
}

if (import.meta.env.NODE_ENV === 'development') {
    // In development mode, use a global variable
    // so that the value is preserved across module reloads
    // caused by HMR (Hot Module Replacement).
    if (!global._mongoClientPromise) global._mongoClientPromise = client.connect();
    clientPromise = global._mongoClientPromise;
} else {
    // In production mode, it's best to
    // not use a global variable.
    clientPromise = client.connect();
}

// Export a module-scoped database instance.
// By doing this in a separate module,
// the database instance can be shared across functions.
export default await (await clientPromise).db();

You would then use it this way:

import db from '$lib/server/db/db';

export const table_users = 'users';

export async function createUser(user: User): Promise<WithId<User>> {
  const res = await db.collection(table_users).insertOne(user);
  /* ... */
}
Ennoriel
  • 144
  • 1
  • 8