DEV Community

Cover image for Connect to MongoDB Atlas from SvelteKit
kvetoslavnovak
kvetoslavnovak

Posted on • Edited on

Connect to MongoDB Atlas from SvelteKit

MongoDB Atlas recently published new best practices how to connect to MongoDB Atlas database. If you would like to know how to apply this recommendations in SvelteKit using MongoDB Atlas database read on.

Start a new SvelteKit project in your console called for example SvelteKit_MongoDB_Atlas

npm init svelte@next SvelteKit_MongoDB_Atlas
cd SvelteKit_MongoDB_Atlas
npm install
npm run dev -- --open
Enter fullscreen mode Exit fullscreen mode

To communicate with MongoDB database we will need Node driver mongodb. So install it.

npm install mongodb
Enter fullscreen mode Exit fullscreen mode

I assume you have set up your MongoDB Atlas database. Their Starter Cluster is free. They have a clear tutorial how to do this. Or just follow the instructions after your sign up, it is pretty straightforward. You may create a new database, e.g. "Todos" with a new collection e.g. "sveltekit-todos".

To connect to MongoDB Atlas you need to use a so called MONGODB_URI. MongoDB Atlas gave you this link when you have set up your database there. It should look something like this:

MONGODB_URI = "mongodb+srv://<username>:<password>@cluster0....."
Enter fullscreen mode Exit fullscreen mode

As you see it contains some sensitive data (username, password). So our project needs features to keep it secret, keep it save. Best way is to use environmental variables. You usually store them in .env file in the root directory of your project. SvelteKit is using Vite under the hood. Vite has its own implementation of environmental variables. But it may be considered a little bit tricky.

So our project will need some other tool. Let install good old dotenv.

npm install dotenv
Enter fullscreen mode Exit fullscreen mode

In the root directory of your project create a new file .env and insert your MONGODB_URI. Specify also database name (DB_NAME). Following recommendation from MongoDB Atlas specify that we use it in development (NODE_ENV) as well, etc.

MONGODB_URI = "mongodb+srv://<username>:<password>@cluster0....."
DB_NAME = "Todos"
NODE_ENV = "development" // "production", "testing"
DEV_URL = "http://localhost:3000"
PROD_URL = "https://<Vercel_Project_Name>.vercel.app"
Enter fullscreen mode Exit fullscreen mode

Finally we have arrived to the implementation of the MongoDB Atlas best practices. One problem this tries to solve is not to open more connections to the database at the same time, especially in a serverless environment like Vercel, so it will not crash eventually.

In your lib directory (which is in a src directory) create a new file mongodb-client.js. Copy inside the code adviced by MongoDB Atlas best practices but with a slight modification.

import dotenv from 'dotenv';
dotenv.config();

import { MongoClient } from 'mongodb';

const uri = process.env['MONGODB_URI'];
const options = {
    useUnifiedTopology: true,
    useNewUrlParser: true,
}

let client
let clientPromise

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

if (process.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) {
        client = new MongoClient(uri, options)
        global._mongoClientPromise = client.connect()
    }
    clientPromise = global._mongoClientPromise
} else {
    // In production mode, it's best to 
    // not use a global variable.
    client = new MongoClient(uri, options)
    clientPromise = client.connect()
}

// Export a module-scoped MongoClient promise. 
// By doing this in a separate module, 
// the client can be shared across functions.
export default clientPromise
Enter fullscreen mode Exit fullscreen mode

You may have noticed that we made one very important modification concerning how we are referencing environmental variables:

process.env['MONGODB_URI'] // this works
process.env.MONGODB_URI //this does not work!!!
Enter fullscreen mode Exit fullscreen mode

In your routes folder create index.js file. We are now ready to define an endpoint. To make it easy and keep this example rather simple we will insert one task "Walk the dog" to a collection "sveltekit-todos" in MongoDB Atlas using post method. I expect you to code the frontend index.svelte page sending the post request by yourself.

import clientPromise from '../../lib/mongodb-client'

export async function post (request) {
    const dbConnection = await clientPromise;
    const db = dbConnection.db();
    const collection = db.collection('sveltekit-todos');
    const todo = JSON.parse(request.body); // “Walk the dog”
    const newTodo = await collection.insertOne(todo);
    return {
        status: 200,
        body: {
            newTodo
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If you open the index page of the project the developer’s tools console of the page should notify you that the insertion was successful, like this:

{"todos":{"acknowledged":true,"insertedId":"617fd886f......."}}
Enter fullscreen mode Exit fullscreen mode

I hope this was helpful.

Top comments (10)

Collapse
 
mawoka profile image
Mawoka

I am getting the error that global is not defined. Any idea why?

Collapse
 
kvetoslavnovak profile image
kvetoslavnovak • Edited

I am not sure. When exactly are you getting this error?

Collapse
 
todd_fincannon profile image
Todd Fincannon

I encountered the same problem. It turns out I was doing the mongodb import in a Svelte component, where global is not present. It works fine in a JS endpoint — which is where it would go in a real app.

Thread Thread
 
kvetoslavnovak profile image
kvetoslavnovak • Edited

Sure thing, never connect directly to your database from the client. That would be really dangerous practice.

Collapse
 
kolosseo profile image
kolosseo

Thank you for the post. Anyway, I need to show two errors in the code:

  • I had to add async before client.connect() otherwise it didn't work.
  • dbConnection is undeclared. It has to be substituted with clientPromise (probably a typo).
Collapse
 
kvetoslavnovak profile image
kvetoslavnovak • Edited

Thank you for your comment on typo. I have corrected it.
I did not need the async to have a working code but I guess it depends, the official documentation does not mention it.

Collapse
 
mohamadfarbod profile image
Frbd

Thanks for the tutorial,
where is the lid and scr directory ?

Collapse
 
kvetoslavnovak profile image
kvetoslavnovak • Edited

These folders are part of a default standard SvelteKit directories structure. You may check a documentation here:
kit.svelte.dev/docs/modules#$lib

Some comments may only be visible to logged-in visitors. Sign in to view all comments.