Create a CRUD Application with Golang and MongoDB on Ubuntu 20.04
Introduction
MongoDB is one of the best document-based open-source database management systems. You can use the application across a wide range of business systems due to its flexible design patterns, which are not met by traditional SQL databases.
With its robust database schema, the MongoDB platform is suitable for setting up systems for managing products' data, creating content management systems (CMS), and in most cases, storing and querying big data.
On the other hand, Golang is a fast, scalable, and easy-to-learn modern programming language for coding advanced software applications. Since MongoDB provides a comprehensive API for the Golang language, you can use the two applications together to come up with solutions for finance, e-commerce, research, and more.
In this guide, you'll set up a database with MongoDB and communicate with it from a custom Golang application to perform basic create, read, update, and delete (CRUD) operations on your Ubuntu 20.04 server.
Prerequisites
To proceed with this guide, ensure you have got the following:
- An Ubuntu 20.04 Server.
- A non-root user with sudo privileges.
- A MongoDB database configured with a user account and password.
- A Golang package.
1. Create a MongoDB Database
Your data-driven application must be able to store, retrieve, update, and delete records. You can only perform all these operations after setting up a database for your software. SSH to your server and follow the steps below to initialize a MongoDB database.
Log in to your MongoDB database. Replace
mongo_db_admin
with the admin account for your database.$ mongosh -u mongo_db_admin -p --authenticationDatabase admin
Enter a password for your MongoDB account when prompted and press Enter to proceed. Next, run the statement below to create a
shop_db
database.test> use shop_db
Confirm that you've switched to the new
shop_db
database.switched to db shop_db
Next, insert three documents in a new
products
collection by using the MongoDBinsertMany()
function.shop_db> db.products.insertMany([ {"product_id" : 1, "product_name" : "LEATHER BELT", "retail_price" : 24.35 }, {"product_id" : 2, "product_name" : "WINTER JACKET", "retail_price" : 99.95 }, {"product_id" : 3, "product_name" : "WOOLEN SWEATER", "retail_price" : 43.20 } ]);
Make sure the command has succeeded by confirming the output below.
{ acknowledged: true, insertedIds: { '0': ObjectId("62188b2358979df39bbcf178"), '1': ObjectId("62188b2358979df39bbcf179"), '2': ObjectId("62188b2358979df39bbcf17a") } }
Next, use the following statement to query the
products
collection to make sure the data is in place.shop_db> db.products.find()
You should get a list of all products together with the associated
_ids
as follows.[ { _id: ObjectId("62188b2358979df39bbcf178"), product_id: 1, product_name: 'LEATHER BELT', retail_price: 24.35 }, { _id: ObjectId("62188b2358979df39bbcf179"), product_id: 2, product_name: 'WINTER JACKET', retail_price: 99.95 }, { _id: ObjectId("62188b2358979df39bbcf17a"), product_id: 3, product_name: 'WOOLEN SWEATER', retail_price: 43.2 } ]
Log out from the MongoDB server.
shop_db> quit
You've now set up the
shop_db
database,products
collection, and sample documents. In the next steps, you'll create some scripts using the Golang language to manipulate your MongoDB collection.
2. Create a main.go
File
The main.go
file will hold the main()
function for your application. This is the main method that fires when you execute your application.
Before you create the source code for the
main.go
file, create aproject
directory to separate your source code from the rest of the Linux files.$ mkdir project
Then, switch to the new
project
directory.$ cd project
Next, use the
nano
text editor to open a newmain.go
file for editing purposes.$ nano main.go
With the
main.go
file open, enter the information below into the file. Replacemongo_db_admin
andEXAMPLE_PASSWORD
with the correct values for your MongoDB user account.package main import ( "context" "net/http" "encoding/json" _"log" "fmt" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const ( dbUser = "mongo_db_admin" dbPass = "EXAMPLE_PASSWORD" dbName = "shop_db" ) func main() { http.HandleFunc("/api/v1/products", requestHandler) http.ListenAndServe(":8080", nil) } func requestHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") response := map[string]interface{}{} ctx := context.Background() client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://" + dbUser + ":" + dbPass + "@localhost:27017")) if err != nil { fmt.Println(err.Error()) } collection := client.Database(dbName).Collection("products") data := map[string]interface{}{} err = json.NewDecoder(req.Body).Decode(&data) if err != nil { fmt.Println(err.Error()) } switch req.Method { case "POST": response, err = createRecord(collection, ctx, data) case "GET": response, err = getRecords(collection, ctx) case "PUT": response, err = updateRecord(collection, ctx, data) case "DELETE": response, err = deleteRecord(collection, ctx, data) } if err != nil { response = map[string]interface{}{"error": err.Error(),} } enc := json.NewEncoder(w) enc.SetIndent("", " ") if err := enc.Encode(response); err != nil { fmt.Println(err.Error()) } }
Save and close the file when you're through with editing.
In the above file, you're creating a web server that listens for incoming HTTP requests on port
8080
using the statementshttp.HandleFunc("/api/v1/products", requestHandler)
andhttp.ListenAndServe(":8080", nil)
.Under the
requestHandler()
function, you're connecting to the MongoDB instance that you created earlier. Next, you're using the Golangswitch
statement to route the HTTP request to the respective CRUD functions by passing aproducts
collection reference. Finally, you're using the JSON function to format and output the data in a human-readable format.Now that you've created the
main.go
file, you'll now set up individual functions on different files to handle all CRUD operations for your application.
3. Set Up a New create_record.go
File
The first file you're going to set up for the CRUD operations is the create_record.go
file. This file contains a function for inserting documents into the products
collection.
Run the command below to set up the
create_record.go
file.$ nano create_record.go
Next, enter the information below into the file.
package main import ( "context" "go.mongodb.org/mongo-driver/mongo" ) func createRecord(collection *mongo.Collection, ctx context.Context, data map[string]interface{})(map[string]interface{}, error){ req, err := collection.InsertOne(ctx, data) if err != nil { return nil, err } insertedId := req.InsertedID res := map[string]interface{}{ "data" : map[string]interface{}{ "insertedId": insertedId, }, } return res, nil }
Save and close the file.
The main function in the above file is
collection.InsertOne(ctx, data)
which saves the BSON payload from the requesting HTTP client to the MongoDB database. Under the above file, you're returning theinsertedId
of the new document if the statement executes without any errors.Next, you'll set up a function to remove documents from the MongoDB collection.
4. Create a delete_record.go
File
As with any other application, you must provide a function to delete records from the products
collection if you no longer need them.
Open a new
delete_record.go
file usingnano
.$ nano delete_record.go
Next, enter the information below into the
delete_record.go
file.package main import ( "context" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/bson" ) func deleteRecord(collection *mongo.Collection, ctx context.Context, data map[string]interface{})(map[string]interface{}, error){ _, err := collection.DeleteOne(ctx, bson.M{"product_id": data["product_id"]}) if err != nil { return nil, err } res := map[string]interface{}{ "data" : "Document deleted successfully.", } return res, nil }
Save and close the file.
In the file above, you're using the function
collection.DeleteOne(...)
to delete a document from the MongoDB database. To ensure you're deleting the right document, you're retrieving theproduct_id
of the item you want to delete using the statementbson.M{"product_id": data["product_id"]}
. In other words, you should pass aproduct_id
in the HTTP payload when submitting aDELETE
request to the application.Next, you'll set up a function to update the documents.
5. Create a New update_record.go
File
You'll use the update_record.go
file to make changes to your documents. The function(updateRecord()
) under this file relies on the payload that contains the fields that you want to update as well as the unique product_id
of the document.
Use
nano
to open up a newupdate_record.go
file.$ nano update_record.go
Next, enter the following information into the
update_record.go
file.package main import ( "context" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) func updateRecord(collection *mongo.Collection, ctx context.Context, data map[string]interface{})(map[string]interface{}, error){ filter := bson.M{"product_id": data["product_id"]} fields := bson.M{"$set": data} _, err := collection.UpdateOne(ctx, filter, fields) if err != nil { return nil, err } res := map[string]interface{}{ "data" : "Document updated successfully.", } return res, nil }
In the above file, you're first providing a
filter
parameter for the document that you want to update using the statementfilter := bson.M{"product_id": data["product_id"]}
. Then, you're submitting the new document values using the statementfields := bson.M{"$set": data}
. Thedata
value here comes from an HTTP payload as submitted by the requesting client.Next, you're using the function
collection.UpdateOne(ctx, filter, fields)
to submit an update request to your collection. In the next step, you'll create a function for retrieving records from your MongoDB collection.
6. Create a New get_records.go
File
The MongoDB API for Golang comes with very intuitive functions for retrieving documents from the database in the form of a map. You'll use these functions to query your database collection and return the documents to the main.go
file that you created earlier.
Use
nano
to create a newget_records.go
file.$ nano get_records.go
Then, enter the following information into the
get_records.go
file.package main import ( "context" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) func getRecords(collection *mongo.Collection, ctx context.Context)(map[string]interface{}, error){ cur, err := collection.Find(ctx, bson.D{}) if err != nil { return nil, err } defer cur.Close(ctx) var products []bson.M for cur.Next(ctx) { var product bson.M if err = cur.Decode(&product); err != nil { return nil, err } products = append(products, product) } res := map[string]interface{}{} res = map[string]interface{}{ "data" : products, } return res, nil }
Save and close the file.
In the above file, you're using the function
cur, err := collection.Find(ctx, bson.D{})
to return a cursor of the documents that you've saved in yourproducts
collection. Then, you're using thefor cur.Next(ctx) {...}
loop to iterate through the documents which you're later appending to theproducts []bson.M
array.Towards the end, you're returning the data back to the calling function as a map of
[string]interface{}
. You've now set up all the CRUD functions for your app. In the next step, you'll test the application to make sure everything is working as expected.
7. Test the Golang Application
In this step, you'll test the application to make sure it can handle all CRUD operations without any errors.
Import the MongoDB driver for your Golang application.
$ go get go.mongodb.org/mongo-driver/mongo
Next, execute the following command to run your application. The command below allows your application to start a web server and listen for incoming HTTP connections on port
8080
and has a blocking function. Don't run any other command on this SSH terminal window.$ go run ./
Next, establish a new SSH session to your server in a separate terminal window.
Attempt creating a new document by running the
curl
command below.$ curl -X POST localhost:8080/api/v1/products -H "Content-Type: application/json" -d '{"product_id": 4, "product_name": "WIRELESS KEYBOARD", "retail_price": 45.30}'
You should get an
insertedId
of the new record, as shown below.{ "data": { "insertedId": "621c9acf3f4e8882c3eeabef" } }
Next, use the command below to retrieve all documents from the
products
collection.$ curl -X GET localhost:8080/api/v1/products
You should now see a list of four documents. The first three are the documents that you set up when you first initialized the database, and the last record(
WIRELESS KEYBOARD
) is the one that you've just inserted using thecurl
command.{ "data": [ { "_id": "621c9aaf35ece941bcc5b80d", "product_id": 1, "product_name": "LEATHER BELT", "retail_price": 24.35 }, { "_id": "621c9aaf35ece941bcc5b80e", "product_id": 2, "product_name": "WINTER JACKET", "retail_price": 99.95 }, { "_id": "621c9aaf35ece941bcc5b80f", "product_id": 3, "product_name": "WOOLEN SWEATER", "retail_price": 43.2 }, { "_id": "621c9acf3f4e8882c3eeabef", "product_id": 4, "product_name": "WIRELESS KEYBOARD", "retail_price": 45.3 } ] }
Next, run the command below to update the document with a
product_id
of1
and change itsproduct_name
fromLEATHER BELT
toMETAL BUCKLE LEATHER BELT
.$ curl -X PUT localhost:8080/api/v1/products -H "Content-Type: application/json" -d '{"product_id": 1, "product_name": "METAL BUCKLE LEATHER BELT", "retail_price": 45.30}'
The following output confirms that you've updated the product details successfully.
{ "data": "Document updated successfully." }
Delete the document with a
product_id
of4
(WIRELESS KEYBOARD
) by running the command below.$ curl -X DELETE localhost:8080/api/v1/products -H "Content-Type: application/json" -d '{"product_id": 4}'
You should get the following confirmation message.
{ "data": "Document deleted successfully." }
Next, retrieve the records one more time to make sure you've effected the
UPDATE
andDELETE
operations in theproducts
collection.$ curl -X GET localhost:8080/api/v1/products
As you can see from the output below, you've deleted the document with a
product_id
of4
, and you've also successfully updated the value of the document with aproduct_id
of1
toMETAL BUCKLE LEATHER BELT
.{ "data": [ { "_id": "621c9aaf35ece941bcc5b80d", "product_id": 1, "product_name": "METAL BUCKLE LEATHER BELT", "retail_price": 45.3 }, { "_id": "621c9aaf35ece941bcc5b80e", "product_id": 2, "product_name": "WINTER JACKET", "retail_price": 99.95 }, { "_id": "621c9aaf35ece941bcc5b80f", "product_id": 3, "product_name": "WOOLEN SWEATER", "retail_price": 43.2 } ] }
Your code is now working as expected, and it is able to handle all CRUD operations.
Conclusion
In this guide, you've used the Golang programming language to connect and manipulate data in a MongoDB collection on your Ubuntu 20.04 server. Use the functions in this guide when designing your next data-driven MongoDB application with Golang.