Home > Software engineering >  How do I use the handles that I have connected to my mongodb and collections in another function?
How do I use the handles that I have connected to my mongodb and collections in another function?

Time:12-15

I have a database that I've setup on mongo which is seeded with some data I need to query via a url parameter from an endpoint. In order to use the library, I had defined some handles and did the whole setup for the connection of the db in a separate setup() function, but I can't use the handles I require outside of it.

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/gorilla/mux"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/mongo/readpref"
)

func setup() {
    clientOptions := options.Client().
        ApplyURI("mongodb srv://<username>:<password>@cluster0.um5qb.mongodb.net/<db>?retryWrites=true&w=majority")
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    client, err := mongo.Connect(ctx, clientOptions)
    if err != nil {
        log.Fatal(err)
    }
    err = client.Ping(ctx, readpref.Primary())
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctx)
    // DB := client.Database("cities-nighthack")
    // Cities := DB.Collection("city")

}

// model for user endpoint
type User struct {
    Email string `json:"email"`
}

// fake db to temp store users
var users []User

// checks if json is empty or not
func (u *User) IsEmpty() bool {
    return u.Email == ""
}

type App struct {
    Mongo *mongo.Client
}

func main() {
    setup()
    r := mux.NewRouter()
    r.HandleFunc("/user", createUser).Methods("POST")
    // r.HandleFunc("/suggest?city_name={city}", searchCity).Methods("GET")

    fmt.Println("Server running at port 8080")
    log.Fatal(http.ListenAndServe(":8080", r))

}

func createUser(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    if r.Body == nil {
        json.NewEncoder(w).Encode("Must send data")
    }

    var user User
    err := json.NewDecoder(r.Body).Decode(&user)
    if err != nil {
        log.Fatal(err)
    }
    if user.IsEmpty() {
        json.NewEncoder(w).Encode("Invalid! Enter user email.")
        return
    }
    users = append(users, user)
    json.NewEncoder(w).Encode(user)

}

func (a *App) searchCity(w http.ResponseWriter, r *http.Request) {

    vars := mux.Vars(r)
    city := vars["city"]
    
}

I figured I'd be able to simply pass the handler like so:

func searchCity(city *mongo.Collection) (w http.ResponseWriter, r *http.Request) {
...
}

However, gmux doesn't allow you to do that since it implicitly passes in http.ResponseWriter and a *http.Request. Therefore, any input can't be in the arguments. I tried declaring them globally but that didn't work and was recommended not to do so. I was told I could try using a closure or a struct to pass it in but I don't quite understand how I'd go about doing that either.

CodePudding user response:

One way to do it is like this, first add a server type

type server struct {
    router *mux.Router
    cities *mongo.Collection
}

Add a routes wrapper to the server

func (s *server) routes() {
    s.router.HandleFunc("/base", s.handleIndex()).Methods("GET")
}

The handler function

func (s *server) handleIndex() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        cities := s.cities.Find(...) // something like that
        // write your response, etc
    }
}

Then in main

func main() {
    sr := &server{
        router: mux.NewRouter(),
        cities: getMongoDBCollection('cities') // implement this one :) should return a *mongo.Collection...
    }
    sr.routes()
...
}
  • Related