Unable to configure Mailgun credentials from database

I’m storing my Mailgun configuration details in my database and need to retrieve the API key, username, and sender email before sending any emails. However, I’m having trouble setting up the configuration properly.

When I try to call the configuration function outside the email module, I get a mongoose buffering timeout error: MongooseError: Operation settings.findOne() buffering timed out after 10000ms

Here’s my current email setup:

let emailClient;

const getEmailConfig = async () => {
    const settings = await Settings.findById({ _id: process.env.SETTINGS_ID });

    let apiKey = settings.email_api_key;
    let user = settings.email_username;

    return mailgun.client({
        username: user,
        key: apiKey,
    });
};

const sendWelcomeEmail = (recipient_email, recipient_name) => {
    getEmailConfig();
    try {
        emailClient.messages
            .create("sandbox2e1ac3c798ed527dce0aef8b8e2cd35d.mailgun.org", {
                from: sender_email,
                to: [recipient_email],
                subject: "Welcome!",
                text: "Welcome! Your account has been created successfully.",
                html: `<p>Hi ${recipient_name}</p> <br /><br /> <p>Welcome! Your account has been created successfully.</p><br /> Best Regards, <br /> Support Team`,
            })
            .then((response) => console.log(response))
            .catch((err) => console.log(err));
    } catch (error) {
        console.log("Email Error: ", error);
    }
};

How can I properly set the mailgun client with database values? The configuration seems to be called before the database connection is ready.

That timeout means your Settings schema probably isn’t indexed on the _id field, or you’re using findById wrong. Try Settings.findById(process.env.SETTINGS_ID) instead of passing an object.

Bigger issue though - you’re creating a new mailgun client for every single email. I’ve been running something similar for two years and this absolutely destroys performance. Cache your config instead:

let cachedConfig = null;
let configExpiry = 0;

const getEmailConfig = async () => {
    if (cachedConfig && Date.now() < configExpiry) {
        return cachedConfig;
    }
    
    const settings = await Settings.findById(process.env.SETTINGS_ID);
    cachedConfig = {
        client: mailgun.client({ username: settings.email_username, key: settings.email_api_key }),
        senderEmail: settings.sender_email
    };
    configExpiry = Date.now() + 300000; // 5 minutes
    return cachedConfig;
};

This stops the database hammering and fixes your async problems. Just make sure sendWelcomeEmail is async and you’re awaiting the config properly.

yeah, your mongoose connection probably isnt ready when getEmailConfig runs. make sure the db connects first before doing any email stuff. also, you need to await getEmailConfig() and store the result in emailClient - thats why its showing up as undefined.

the problem’s in your findById call - you’re passing an object but it just wants the id string. change Settings.findById({ _id: process.env.SETTINGS_ID }) to Settings.findById(process.env.SETTINGS_ID). also make sendWelcomeEmail async and actually await getEmailConfig before using the client.

Your app’s trying to hit the database before the connection’s even ready. Classic race condition - your email functions fire before MongoDB can handle the queries.

I hit this same issue last year. Fix is simple: get your database connection sorted first thing in your startup sequence (usually in your main server file), then use connection events to make sure everything’s actually ready before moving on.

Also, your getEmailConfig function returns a promise but you’re not awaiting it in sendWelcomeEmail. Make that function async and handle the client properly. I’d suggest using a singleton pattern for your email client too - stops you from recreating it every time you send an email and cuts way down on database calls.

Had this exact problem six months ago. The async handling isn’t your real issue - you’re setting up email sending in a way that’ll bite you later.

Your code’s broken in several ways. You call getEmailConfig but don’t use what it returns. emailClient never gets assigned anything. Worst part? You’re spinning up new database connections and API clients for every single email.

I tried fixing the code first, but that’s the wrong approach. You need to redesign the whole flow. Email sending shouldn’t be tied to your main app logic at all.

Latenode solved this for me. Set up a webhook that takes your email requests, grabs the Mailgun config from your database once per batch, then processes everything in the background. No more mongoose timeouts, race conditions, or blocked threads.

Latenode handles database connections properly and auto-retries failed emails. You get monitoring and logging thrown in too. Way better than patching async bugs in your current mess.

You’re calling getEmailConfig() but not waiting for it or using what it returns. Your emailClient never gets set.

You need to await the config function and actually use the client it gives you:

const sendWelcomeEmail = async (recipient_email, recipient_name) => {
    try {
        const client = await getEmailConfig();
        const settings = await Settings.findById({ _id: process.env.SETTINGS_ID });
        
        client.messages
            .create("sandbox2e1ac3c798ed527dce0aef8b8e2cd35d.mailgun.org", {
                from: settings.sender_email,
                to: [recipient_email],
                subject: "Welcome!",
                // rest of your config
            })
    } catch (error) {
        console.log("Email Error: ", error);
    }
};

But this setup’s gonna bite you later. You’re hitting the database every time you send an email. What happens when you need to send 100 emails? 1000? Your database will crash.

I’ve dealt with this before. The cleanest fix is using Latenode for this workflow. Set up a scenario that pulls your Mailgun config once, caches it, and handles all the email sending without hammering your database.

Latenode connects directly to your database and Mailgun API. It refreshes credentials when needed and queues emails properly. No more mongoose timeouts or config headaches.