MongoDB Atlas not connecting with my golang App - mongodb

I'm new to golang and MongoDB i've been following the official blog of mongoDb for getting started with mongo-db driver for go, and i'm not able to connect to my mongodb for some reason
here is the code ,the error is "context deadline exceeded", thnx in advance
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
// "go.mongodb.org/mongo-driver/mongo/readpref"
)
func main() {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb+srv://chandru:<Heregoesmypassword>#cluster0-9jkaf.mongodb.net/test?retryWrites=true&w=majority"))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 20*time.Second)
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(ctx)
databases, err := client.ListDatabaseNames(ctx, bson.M{})
if err != nil {
log.Fatal(err)
}
fmt.Println(databases)
}

One possible cause is that you have not whitelisted your IP with MongoDB Atlas
Please refer to the following guide: https://docs.atlas.mongodb.com/security-whitelist/#view-whitelist-entries
Keep in mind that if you connect from your local machine, your IP address might change. You can get a static IP address from your ISP or connect to through another machine.
You can also enter 0.0.0.0/0 to allow access from any IP, but this is of course less secure.

Related

Can the MongoDB client be reused? [duplicate]

This question already has answers here:
Is mongodb client driver concurrent safe?
(1 answer)
goroutine create multiple mongodb connection
(1 answer)
Closed 21 days ago.
I've managed to connect to MongoDB and I've got this client object, can this be passed around and reused?
Or does it need to be created each time I want to interact with the database?
If it can be reused then ill move the database connection out to its own function and run it just once at startup.
package main
import (
"context"
"fmt"
"log"
"main/api"
"os"
"time"
"github.com/joho/godotenv"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Username string
Password string `bson:"password,omitempty"`
}
func main() {
if err := godotenv.Load(); err != nil {
log.Println("No .env file found")
}
uri := os.Getenv("MONGODB_URI")
serverAPIOptions := options.ServerAPI(options.ServerAPIVersion1)
clientOptions := options.Client().
ApplyURI(uri).
SetServerAPIOptions(serverAPIOptions)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
usersColl := client.Database("sampleShop").Collection("users")
newUser := User{Username: "8282", Password: "Korean"}
result, err := usersColl.InsertOne(context.TODO(), newUser)
if err != nil {
panic(err)
}
fmt.Println(result)
fmt.Println(uri)
api.GetPaymentSession()
}

Mongo client set in main function, functions in other modules receive nil value

I have a restful API utilizing mux and mongo-driver. Following a tutorial, I attempted to setup the server and mongo client like so in the main package:
package main
import (
"context"
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
c "github.com/moonlightfight/elo-backend/config"
"github.com/moonlightfight/elo-backend/routes/admin"
"github.com/moonlightfight/elo-backend/routes/tournament"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
func main() {
// Set the file name of the configurations file
viper.SetConfigName("config")
// Set the path to look for the configurations file
viper.AddConfigPath(".")
// Enable VIPER to read Environment Variables
viper.AutomaticEnv()
viper.SetConfigType("yml")
var configuration c.Configurations
if err := viper.ReadInConfig(); err != nil {
fmt.Printf("Error reading config file, %s", err)
}
err := viper.Unmarshal(&configuration)
if err != nil {
fmt.Printf("Unable to decode into struct, %v", err)
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
clientOptions := options.Client().ApplyURI(fmt.Sprintf("mongodb+srv://%s:%s#cluster0.ucnph.mongodb.net/%s?retryWrites=true&w=majority", configuration.Database.DBUser, configuration.Database.DBPass, configuration.Database.DBName))
port := fmt.Sprintf(":%d", configuration.Server.Port)
mongo.Connect(ctx, clientOptions)
router := mux.NewRouter()
router.HandleFunc("/api/admin", admin.CreateAdminEndpoint).Methods("POST")
router.HandleFunc("/api/admin/login", admin.AdminLoginEndpoint).Methods("POST")
router.HandleFunc("/api/tournament/getfromweb", tournament.GetTournamentData).Methods("GET")
fmt.Printf("server listening on http://localhost%v", port)
http.ListenAndServe(port, router)
}
Now, in order to manage my code more concisely, I set up modules (as you can see in the imports on main) to handle the functions that mux will use for the endpoints.
On one specific case (handling the "/api/admin" endpoint:
package admin
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/dgrijalva/jwt-go"
c "github.com/moonlightfight/elo-backend/config"
m "github.com/moonlightfight/elo-backend/models"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"golang.org/x/crypto/bcrypt"
)
var client *mongo.Client
// other code here
func CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) {
response.Header().Set("content-type", "application/json")
var admin m.Admin
err := json.NewDecoder(request.Body).Decode(&admin)
if err != nil {
log.Println(err)
}
// encrypt user password
admin.Password = HashPassword(admin.Password)
fmt.Println(client)
collection := client.Database("test").Collection("Admin")
ctx, ctxErr := context.WithTimeout(context.Background(), 5*time.Second)
if ctxErr != nil {
log.Println(ctxErr)
}
result, resErr := collection.InsertOne(ctx, admin)
if resErr != nil {
log.Println(resErr)
}
json.NewEncoder(response).Encode(result)
}
And when ran, I receive the following error:
2021/06/05 02:02:39 http: panic serving [::1]:53359: runtime error: invalid memory address or nil pointer dereference
This points to the line where I define collection in the endpoint function, which logged as having a nil value. I am clearly not getting the mongo client properly defined in the module, and not sure of the best practice for maintaining this client connection across multiple modules.
The standard way of doing this while avoiding globals would be to define a struct that represents your server, and its methods would be the handlers. Then the methods share the struct's data, and you place things like your mongo client in there.
Something like this (in your admin package):
type Server struct {
client *mongo.Client
}
func NewServer(client *mongo.Client) *Server {
return &Server{client: client}
}
func (srv *Server) CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) {
// ...
// use srv.client here
//
}
And now in main, you create the server like this:
client, err := mongo.Connect(...)
// handle err!
srv := admin.NewServer(client)
router := mux.NewRouter()
router.HandleFunc("/api/admin", srv.CreateAdminEndpoint).Methods("POST")

How to Manage MongoDB Client across Packages in Golang

I am currently starting out with MongoDB on GoLang. My current use case is this.
I wish to initialize connection to my MongoDB Database in a particular package and use the returned client across several other local packages. Here is what I have tried,
I have initialized the connection to MongoDB inside a package called dataLayer as below
package dataLayer
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
func InitDataLayer() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(credentials.MONGO_DB_ATLAS_URI))
if err != nil {
log.Fatal(err)
} else {
log.Println("Connected to Database")
}
}
Now if I wish to use the returned client in other packages, is it production safe to keep on calling initDataLayer over and over again to get back a client?
Thanks alot.
You don't have to call InitDataLayer over and over again. You need to create only one client, you can use the same client to connect to Mongo DB from various places.
Mongo client internally maintains a pool of connections, so you don't have to create this client again and again.
It's a good design to store this client as the struct field and keep reusing the same.
Edit:
Create Connection
package dataLayer
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
func InitDataLayer()(*mongo.Client) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(credentials.MONGO_DB_ATLAS_URI))
if err != nil {
log.Fatal(err)
} else {
log.Println("Connected to Database")
}
return client
}
main.go,
Close connection at the end of usage, otherwise connection would be leaked.
func main(){
//...
client = dataLayer.InitDataLayer()
defer client.Disconnect(context.Background())
...
}
Repository/DAL, store client in Repository struct
Interface
type BookRepository interface {
FindById( ctx context.Context, id int) (*Book, error)
}
Repository Implementation that store client in struct
type bookRepository struct {
client *mongo.Client
}
func (r *bookRepository) FindById( ctx context.Context, id int) (*Book, error) {
var book Book
err := r.client.DefaultDatabase().Collection("books").FindOne(ctx, bson.M{"_id": id }).Decode(&book)
if err == mongo.ErrNoDocuments {
return nil, nil
}
if err != nil {
return nil, err
}
return &book, nil
}

how to connect compose mongodb with golang

I try to connect compose mongodb in bluemix with golang program but i get unsupported connection url option ssl. Here I give mongodb connection string got from composedb console. How to connect with remote host need syntax for connect?
session, err := mgo.Dial("mongodb://****:****#aws-us-east-1-portal.26.dblayer.com:20258/admin?ssl=true")
if err != nil {
panic(err)
}
defer session.Close()
// Optional. Switch the session to a monotonic behavior.
session.SetMode(mgo.Monotonic, true)
c := session.DB("test").C("people")
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
&Person{"Cla", "+55 53 8402 8510"})
if err != nil {
log.Fatal(err)
}
The error:
panic: unsupported connection URL option: ssl=true
goroutine 1 [running]:
panic(0x2130a0, 0xc82000a840)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
main.main()
/Users/vit/gocode/src/mongmix/mmix.go:19 +0x9b
exit status 2
install "go get gopkg.in/mgo.v2"
package main
///////////////import mongo gopkg//////////////
import (
"fmt"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
//////////// add struct//////////////////////////
type Person struct {
Name string
Phone string
}
///// main function///////////
func main(){
fmt.Println("Hello world");
//////db connection ////////////
session, err := mgo.Dial("localhost:9494")
if err != nil {
panic(err)
}
defer session.Close()
// Optional. Switch the session to a monotonic behavior.
session.SetMode(mgo.Monotonic, true)
///////////////db name/////////////
c := session.DB("sudheer").C("people")
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
&Person{"Cla", "+55 53 8402 8510"})
if err != nil {
log.Fatal(err)
}
result := Person{}
err = c.Find(bson.M{"name": "Ale"}).One(&result)
if err != nil {
log.Fatal(err)
}
fmt.Println("Phone:", result.Phone)
}
IBM Bluemix Compose for Mongodb has a documentation available online here: https://console.bluemix.net/docs/services/ComposeForMongoDB/index.html#getting-started-with-compose-for-mongodb
Step 2 talks about connecting to your compose mongo db. There is a nodejs sample code provided in step 2. You will have to check connection string to mongodb in golang i.e. the syntax in golang.
I found out this article which explains connecting to IBM Compose mongodb from goland: https://www.compose.com/articles/connect-to-mongo-3-2-on-compose-from-golang/
hope this helps.

Golang and MongoDb remote access fail (server returned error on SASL authentication step: Authentication failed.)

I am trying to connect to remote MongoDB database (Mongolab) from Go with mgo library but getting error panic: server returned error on SASL authentication step: Authentication failed. Here is my code
package main
import (
"fmt"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"log"
)
type Person struct {
Name string
Phone string
}
func main() {
session, err := mgo.Dial("mongodb://<dbusername>:<dbpassword>#ds055855.mlab.com:55855")
if err != nil {
panic(err)
}
defer session.Close()
// Optional. Switch the session to a monotonic behavior.
session.SetMode(mgo.Monotonic, true)
c := session.DB("catalog").C("History")
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
&Person{"Cla", "+55 53 8402 8510"})
if err != nil {
log.Fatal(err)
}
result := Person{}
err = c.Find(bson.M{"name": "Ale"}).One(&result)
if err != nil {
log.Fatal(err)
}
fmt.Println("Phone:", result.Phone)
}
How can I fix this? And of course instead of stars in my code i write my login and password.
Please check if you have added users for your Mongolab database instance (https://mongolab.com/databases/catalog#users in case if your DB name is catalog) because by default your users list is empty (account user/password != database user/password).
Also add /<databasename> to the end of your connection string - mongodb://*******:*******#ds045795.mongolab.com:45795/databasename.