I recently started working on a new project where I decided MongoDB would be a great fit for the data persistence layer. The dataset is small, benefits from being structured as nested documents, and is already in the JSON format. I’ve previously worked on projects with similar characteristics but using the mgo driver. The driver worked fanastically in those projects, but it’s unfortunately become unmaintained. After doing a bit more research into alternatives I discovered that there is an official Go implementation of the MongoDB driver — I had to give it a try.
Since, at the time of this writing, there aren’t any easily Google-able tutorials about how to get the MongoDB driver working with a Go project, I decided to write up this blog post for myself for future reference and for anyone looking for a quick start-up guide. Now, that’s not to say that the examples and accompanying GoDoc aren’t fantastic — they’re great, and I would highly recommend reading through them to really learn the details of how to use it, but I wanted something a little more straightforward and in the context of a sample application that I could copy snippets of code from because, you know, developers are lazy.
So, in this blog post I’ll be using the latest Go module features in Go 1.11 to show to download the MongoDB driver v1.0.0-rc1, use it to connect to a database, and perform the CRUD operations necessary to get this To-do CLI program working:
Let’s get started.
First, you’ll have to download, install, and run MongoDB on your machine. That’s out of the scope of this blog post, but the documentation on how to do it pretty straightforward. If you don’t want to go through all of that action, then you can create a free account with mLab and use one of their sandbox plan types. Once you have MongoDB installed, or mLab setup, create a user with
readWrite privileges of a database called
todo. Next, let’s see what the steps are to initialize the
todocli module that will hold all this code:
$ mkdir todocli
$ cd todocli
$ touch main.go
$ go mod init github.com/wemgl/todocli
Modules are out of the scope of this post, but you can see this wiki on GitHub for a more in-depth overview of what they are and how they work in version 1.11 of the language. Also, if you’re a fan of the YouTube channel JustForFunc, as I am, then check out episodes #42 and #43 about Go modules and semver.
Okay, so once the
todocli module has been created, let’s add the MongoDB driver package as a dependency. You do that in Go 1.11 with:
$ go get firstname.lastname@example.org
That command triggers a download of the specified package and all indirect dependencies for you. It also updates the go.mod file as it makes those changes so you don’t have to. When that download process is complete you can import and use “go.mongodb.org/mongo-driver/mongo” in your source files as you would any other package.
Connecting to your database is fairly straightforward after the driver dependency has been satisfied and the import added to the source file. Here’s how I handled it:
I usually use the environment to hold configuration details, so the parts of the URI for connecting to the mLab MongoDB service are stored there, retrieved using the
os package, and then passed along using context, to the
configDB function which I call from
mongo.Database initialization process requires a
context.Context object, so it made sense to wrap the values for initialization of the DB in the given context. Here’s a look at
The MongoDB driver makes it easy to connect to a database using the typical connection string format. There are of course a few errors returned that must be handled, so after performing all that ceremony, get a reference to the
*mongo.Database by name at the end of the function and return it. All interactions with the database will be through this reference
Create a To-do
Let’s explore how to create a to-do task with the database reference from the previous section. It looks like this:
There is a method,
*database.Collection, that retrieves a handle for a given collection in the database by its name. Once you have the handle to the collection you can perform the various CRUD operations by passing in a
context.Context and a your query document. There is a helper type,
bson.E, that allows for quick document construction on the
bson type. The
primitive.DateTime type is exposed by the
primitive package to wrap datetime values. Together, a new task is named and the created and last updated date are added.
Update a To-do
The steps to update a document are similar to creating one, but instead of
InsertOne there is a convenient
UpdateOne method. It works like so:
It’s easier to find a document to update by object ID in this case, because the value is fixed and fairly compact, so after getting the ID of which element to update from the user, initialize
idDoc to a new document and use that to find the document to update, and then update it. You can see that the driver makes it easy to reuse standard Go features such as anonymous structs to update documents and supply arguments to Mongo operators.
Delete a To-do
Deleting a task is similar to updating one. You find a single document by some criteria, below that criteria is its ID, and then call the
Finally, to list all documents in a collection the driver exposes a
collection.Find method that returns a
mongo.Cursor that can be use to iterate over a collection of documents via its
Next method. The process looks like this:
Directly marshalling an element into a
todo type isn’t as straightforward as it sounds, although the driver does expose a few methods for retrieving a byte slice and JSON representations of the document. It’s actually easier to look up each JSON property from the element document and assign it’s value to the struct fields directly as shown.
And that’s how to perform the basic CRUD operations on your MongoDB database with the official MongoDB Go driver folks. As you saw, the driver has a nice API, supports context for workflows that may need cancellation or timeouts, and allows document manipulation in a familiar way that’s close to the CLI version.
The source code for the resulting To-do CLI for this post can be found on my GitHub, here. You can review it there to see what the entire process of to-do creation, reading, updating, and deletion looks like in context, and what the other functionality for getting user input and displaying tasks looks like. Let me know if you have any questions or if you found this post helpful in any way in the comments.