5
votes

I am trying to connect to mongodb atlas from firebase functions like.

export default async () => {
  try {
    const url = 'mongodb+srv://foo:[email protected]/my-db?retryWrites=true';
    const client = await MongoClient.connect(url);
    client.dbName('my-db');
    return client;
  } catch (e) {
    throw e;
  }
}

However, I am getting this error:

{ "code": "ESERVFAIL", "errno": "ESERVFAIL", "syscall": "querySrv", "hostname": "_mongodb._tcp.foo-cluster.mongodb.net" }

  1. I made sure that my firebase plan is set to Blaze so I can connect to any client outside of google network.
  2. I whitelisted the functions' IP in mongodb atlas dashboard, I also added "connect from everywhere" just to make sure.
  3. I am using nodejs mongo driver version ^3.1.0-beta4

Any thoughts? Thanks.

3
Why are you using a "beta" driver. Also the connection string is not for you. You almost certainly don't want the retryWrites option, and you probably don't want the my-db on the end if you used the Atlas console to create users, because they should be in the default "admin" database. Also client.db('my-db') ( which is another correction ) does not "persist". You want to return the client instance and select the "database" in different code. The error itself seems more indicative of a communication problem. Probably because you have not added the remote host to the whitelist.Neil Lunn
Just realized that 3.1.0-beta4 is actually marked as stable on npm. It shouldn't be. Please also make sure you install 3.0.8 explicitly until somebody fixes that.Neil Lunn
@NeilLunn thanks for the pointers.CENT1PEDE
@NeilLunn can you suggest of a better way to export the db instance after the successfull connection?CENT1PEDE
You probably should not. Exporting the client context is better and then you simply select the .db() from that. It's not really an "instance" anyhow, as nothing communicates with the server after connection until you call a collection method, or something else that does something.Neil Lunn

3 Answers

8
votes

There are few caveats when connecting to Atlas from Firebase Function. Below is the correct way to return a connected client instance for further use in your FB function:

import { MongoClient } from 'mongodb'

const uri = 'mongodb://<USER>:<PASSWORD>@foo-shard-00-00-xxx.gcp.mongodb.net:27017,foo-shard-00-01-xxx.gcp.mongodb.net:27017,foo-shard-00-02-xxx.gcp.mongodb.net:27017/test?ssl=true&replicaSet=FOO-shard-0&authSource=admin&retryWrites=true'

let client

export default async () => {

    if (client && client.isConnected()) {
        console.log('DB CLIENT ALREADY CONNECTED')

    } else try {
        client = await MongoClient.connect(uri, { useNewUrlParser: true })
        console.log('DB CLIENT RECONNECTED')
    }

    catch (e) {
    throw e
    }

    return client
}

Explanation:

  1. reportedly, you cannot connect to Atlas if you are on a Spark plan. Make sure you upgrade to Blaze if you didn't yet.

  2. uri string – You should not use the shortened url format when connecting to Atlas from Firebase. For some reason, only the older, long url format works reliably from firebase.

  3. client variable – You should define the client variable outside the export scope, and then assign the connected client instance to it inside the function, only if it is not already assigned. This will prevent reconnecting the client on every function invocation. Firebase functions are stateless, but not entirely. they only get shut down after some period of inactivity. This means that the connection will persist for some time. From docs: If you declare a variable in global scope, its value can be reused in subsequent invocations without having to be recomputed.

1
votes

To solve this issue, i did:

  • Enable Billing for your project, it will automatically enable the Outbound Network Requests for your app.

Outbound Networks Requests are free up to 5gb/month. So just enable billing and enjoy.

More info about billing here. https://firebase.google.com/pricing#blaze-calculator

1
votes

In my case, the network access rules of my mongodb atlas cluster didn't allow firebase function to access the database. I had to allow access from anywhere to get it working.