
I wanted to learn how to create a rest Api with Golang and MongoDb, found a lot of tutorials using themgo library but as this was unmaintained. I used theMongo driver instead withgorilla/mux. First set up package and imports.
packagemainimport("fmt""log""net/http""context""encoding/json""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""github.com/gorilla/mux""github.com/austincunningham/GolangUserRestApi/models")
Setup the routes and Mongo db connection
// setting a global DB struct to be accessible to route functionstypeDBstruct{collection*mongo.Collection}// Define the routesfuncmain(){fmt.Printf("REST API User from golang\n")// Set client optionsclientOptions:=options.Client().ApplyURI("mongodb://localhost:27017")// Connect to MongoDBclient,err:=mongo.Connect(context.TODO(),clientOptions)iferr!=nil{log.Fatal(err)}// Check the connectionerr=client.Ping(context.TODO(),nil)iferr!=nil{log.Fatal(err)}deferclient.Disconnect(context.TODO())collection:=client.Database("golang-user").Collection("users")db:=&DB{collection:collection}fmt.Println("Connected to MongoDB!")//outputsfmt.Printf("Server listing on http://localhost:8080/users")fmt.Printf("\nCTRL C to exit\n")// Controller for endpointsr:=mux.NewRouter()r.HandleFunc("/users",db.AllUsers).Methods("GET")r.HandleFunc("/users/{id}",db.FindUser).Methods("GET")r.HandleFunc("/users",db.CreateUser).Methods("POST")r.HandleFunc("/users/{id}",db.UpdateUser).Methods("PUT")r.HandleFunc("/users/{id}",db.DeleteUser).Methods("DELETE")iferr:=http.ListenAndServe(":8080",r);err!=nil{log.Fatal(err)}}
Setup the model package
packagemodelsimport("go.mongodb.org/mongo-driver/bson/primitive")// the properties in Mongodb documenttypeUserstruct{Idprimitive.ObjectID`json:"_id,omitempty" bson:"_id,omitempty"`Namestring`bson:"name" json:"name"`Emailstring`bson:"email" json:"email"`Passwordstring`bson:"password" json:"password"`}
Back in the main package define the
GET all users function
// find all usersfunc(db*DB)AllUsers(reshttp.ResponseWriter,req*http.Request){fmt.Println("AllUsers GET")// create an array of usersvarresults[]models.Uservarusermodels.User// set the api headerres.Header().Set("content-type","application/json")// set the find options, not sure I need thisfindOptions:=options.Find()// use the find command to get allresult,err:=db.collection.Find(context.TODO(),bson.D{{}},findOptions)iferr!=nil{fmt.Println("AllUsers GET failed to query DB",err)}//go through the result and decode each element at a timeforresult.Next(context.TODO()){err:=result.Decode(&user)iferr!=nil{log.Fatal(err)}// add to the arrayresults=append(results,user)}//return the array as jsonjson.NewEncoder(res).Encode(results)}
GET find user by id function
// find a single userfunc(db*DB)FindUser(reshttp.ResponseWriter,req*http.Request){fmt.Println("FindUser GET")varusermodels.Userparams:=mux.Vars(req)objectId,_:=primitive.ObjectIDFromHex(params["id"])res.Header().Set("content-type","application/json")filter:=bson.M{"_id":objectId}err:=db.collection.FindOne(context.TODO(),filter).Decode(&user)iferr!=nil{fmt.Println("error",err)}json.NewEncoder(res).Encode(user)}
DELETE user by id is pretty similar
func(db*DB)DeleteUser(reshttp.ResponseWriter,req*http.Request){fmt.Println("DeleteUser DELETE")params:=mux.Vars(req)objectId,_:=primitive.ObjectIDFromHex(params["id"])res.Header().Set("content-type","application/json")filter:=bson.M{"_id":objectId}result,err:=db.collection.DeleteOne(context.TODO(),filter)iferr!=nil{fmt.Println("DeleteUser DELETE failed to query DB",err)}json.NewEncoder(res).Encode(result)}
PUT update user by id
func(db*DB)UpdateUser(reshttp.ResponseWriter,req*http.Request){fmt.Println("UpdateUser PUT")varusermodels.User// get the id from the urlparams:=mux.Vars(req)objectId,_:=primitive.ObjectIDFromHex(params["id"])// set the header infores.Header().Set("content-type","application/json")//set the filter on the idfilter:=bson.M{"_id":objectId}// decode the request body_=json.NewDecoder(req.Body).Decode(&user)update:=bson.M{"$set":&user}result,err:=db.collection.UpdateOne(context.TODO(),filter,update)iferr!=nil{fmt.Println("UpdateOne PUT failed to query DB ",err)}json.NewEncoder(res).Encode(result)}
POST create user
func(db*DB)CreateUser(reshttp.ResponseWriter,req*http.Request){fmt.Println("CreateUser POST")varusermodels.Userres.Header().Set("content-type","application/json")_=json.NewDecoder(req.Body).Decode(&user)result,err:=db.collection.InsertOne(context.TODO(),user)iferr!=nil{fmt.Println("CreateUser Error inserting record ",err)}json.NewEncoder(res).Encode(result)}
So the rest api is all setup at this point to connect to my local MongoDb.
Setup the container and deploy to Openshift
First I set up a project in Openshift 3.11, onMinishift
oc new-project golanguser
Login to the Openshift console and deploy Mongodb from the Openshift catalog. Open the project in the console and click onBrowse Catalog
Click on Mongodb
Follow the creation flow until you get toConfiguration
set the following as some will be auto generated if you don't
- Database Service Name
- MongoDB Connection Username
- MongoDB Connection Password
- MongoDB Database Name
- MongoDB Admin Password
Once set click the create button
The Results screen will have the connection url. I will be using part of the connection url in the code.
Change the connection url in the main package here
// Set client optionsclientOptions:=options.Client().ApplyURI("mongodb://myadmin:password@mongodb:27017")
And make sure that your collection is pointing at the correct db
collection:=client.Database("golanguser").Collection("users")
Once these are set we are ready to create the golang container with the followingdockerfile
# Start from the latest golang base imageFROM golang:latest# Set the Current Working Directory inside the containerWORKDIR /app# Copy go mod and sum filesCOPY go.mod go.sum ./# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changedRUN go mod download# Copy the source from the current directory to the Working Directory inside the containerCOPY..# Build the Go appRUN go build-o main.# Expose port 8080 to the outside worldEXPOSE 8080# Command to run the executableCMD["./main"]
Build the image and push it to a remote registry
docker build-t austincunningham/golanguserrestapi:latest.docker push austincunningham/golanguserrestapi:latest
You can then add the Image to project in the Openshift consoleAdd to Project/Deploy Image
Enter the Image Name click the search icon and deploy when the image is found
Once the container is deployed you can test the connection between MongoDb and the rest api as follows
oc project golanguseroc get podsNAME READY STATUS RESTARTS AGEgolanguserrestapi-1-7px9p 1/1 Running 1 14hmongodb-1-s9fkd 1/1 Running 6 8d# log into the podocexec-it golanguserrestapi-1-7px9p bash# curl and POST some datacurl--header"Content-Type: application/json"--request POST--data'{"name":"austin","email":"austin@austin.com","password":"password"}' http://localhost:8080/users# successful response{"InsertedID":"5ef7a5dba99a0cb3de9302dc"}# curl the GET all endpointcurl http://localhost:8080/users[{"_id":"5ef7a5dba99a0cb3de9302dc","name":"austin","email":"austin@austin.com","password":"password"}]
Final step is to add a route click on the rest container andcreate route
Except the defaults your route will appear in the console it can be accessed from any where with the users route
Code liveson github and the container imageon dockerhub
Top comments(1)
- Email
- LocationSwitzerland
- EducationMaster's degree in ECE
- WorkSoftware Engineer at Intersys AG
- Joined
wow great stuff, thanks Austin! Always great to have a working example 😀
For further actions, you may consider blocking this person and/orreporting abuse