MongoDB with Golang
Connect MongoDB with Go using the official MongoDB Go Driver. Learn how to set up connections, define struct models with BSON tags, perform CRUD operations, handle aggregation, implement pagination, and build a production-ready Go API backed by MongoDB.
Go's performance and simplicity make it an excellent choice for building backend services. The official MongoDB Go Driver provides a type-safe, idiomatic way to interact with MongoDB. This final episode covers everything you need to build production-ready Go applications with MongoDB.
Installation
# Initialize a Go module
mkdir mongo-go-app
cd mongo-go-app
go mod init mongo-go-app
# Install the MongoDB Go Driver
go get go.mongodb.org/mongo-driver/mongo
Connecting to MongoDB
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
func connectDB() *mongo.Database {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var err error
client, err = mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal("Connection failed:", err)
}
// Verify connection
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Ping failed:", err)
}
fmt.Println("Connected to MongoDB!")
return client.Database("blogApp")
}
func disconnectDB() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client.Disconnect(ctx)
}
Defining Models with Struct Tags
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Name string `bson:"name" json:"name"`
Email string `bson:"email" json:"email"`
Age int `bson:"age" json:"age"`
Role string `bson:"role" json:"role"`
Tags []string `bson:"tags,omitempty" json:"tags"`
Address Address `bson:"address,omitempty" json:"address"`
IsActive bool `bson:"isActive" json:"isActive"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
type Address struct {
Street string `bson:"street" json:"street"`
City string `bson:"city" json:"city"`
State string `bson:"state" json:"state"`
Zip string `bson:"zip" json:"zip"`
}
Insert Documents
func insertUser(collection *mongo.Collection) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Insert one
user := User{
Name: "Alice",
Email: "alice@example.com",
Age: 28,
Role: "developer",
Tags: []string{"go", "mongodb"},
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
result, err := collection.InsertOne(ctx, user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Inserted ID: %v\n", result.InsertedID)
// Insert many
users := []interface{}{
User{Name: "Bob", Email: "bob@example.com", Age: 34, Role: "designer", CreatedAt: time.Now()},
User{Name: "Charlie", Email: "charlie@example.com", Age: 25, Role: "developer", CreatedAt: time.Now()},
}
results, err := collection.InsertMany(ctx, users)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Inserted %d documents\n", len(results.InsertedIDs))
}
Query Documents
func findUsers(collection *mongo.Collection) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Find one
var user User
err := collection.FindOne(ctx, bson.M{"email": "alice@example.com"}).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
fmt.Println("User not found")
return
}
log.Fatal(err)
}
fmt.Printf("Found: %s (%s)\n", user.Name, user.Email)
// Find many with options
filter := bson.M{
"role": "developer",
"isActive": true,
}
opts := options.Find().
SetSort(bson.D{{Key: "name", Value: 1}}).
SetLimit(10).
SetProjection(bson.M{"name": 1, "email": 1, "role": 1})
cursor, err := collection.Find(ctx, filter, opts)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
var users []User
if err := cursor.All(ctx, &users); err != nil {
log.Fatal(err)
}
for _, u := range users {
fmt.Printf("- %s (%s)\n", u.Name, u.Role)
}
// Count
count, _ := collection.CountDocuments(ctx, bson.M{"role": "developer"})
fmt.Printf("Developer count: %d\n", count)
}
Update Documents
func updateUsers(collection *mongo.Collection) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Update one
result, err := collection.UpdateOne(
ctx,
bson.M{"email": "alice@example.com"},
bson.M{
"$set": bson.M{"age": 29, "updatedAt": time.Now()},
"$inc": bson.M{"loginCount": 1},
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Modified: %d\n", result.ModifiedCount)
// Update many
result, err = collection.UpdateMany(
ctx,
bson.M{"role": "developer"},
bson.M{"$set": bson.M{"department": "Engineering"}},
)
fmt.Printf("Modified %d developers\n", result.ModifiedCount)
// Find one and update (returns the document)
var updated User
err = collection.FindOneAndUpdate(
ctx,
bson.M{"email": "alice@example.com"},
bson.M{"$inc": bson.M{"views": 1}},
options.FindOneAndUpdate().SetReturnDocument(options.After),
).Decode(&updated)
fmt.Printf("Updated user: %s (views: %d)\n", updated.Name, updated.Age)
}
Delete Documents
func deleteUsers(collection *mongo.Collection) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Delete one
result, err := collection.DeleteOne(ctx, bson.M{"email": "test@example.com"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted: %d\n", result.DeletedCount)
// Delete many
result, err = collection.DeleteMany(ctx, bson.M{"isActive": false})
fmt.Printf("Deleted %d inactive users\n", result.DeletedCount)
}
Aggregation
func aggregate(collection *mongo.Collection) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
pipeline := mongo.Pipeline{
{{Key: "$match", Value: bson.M{"isActive": true}}},
{{Key: "$group", Value: bson.D{
{Key: "_id", Value: "$role"},
{Key: "count", Value: bson.M{"$sum": 1}},
{Key: "avgAge", Value: bson.M{"$avg": "$age"}},
}}},
{{Key: "$sort", Value: bson.M{"count": -1}}},
}
cursor, err := collection.Aggregate(ctx, pipeline)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
var results []bson.M
if err := cursor.All(ctx, &results); err != nil {
log.Fatal(err)
}
for _, r := range results {
fmt.Printf("Role: %v | Count: %v | Avg Age: %.1f\n",
r["_id"], r["count"], r["avgAge"])
}
}
Pagination Helper
type PaginatedResult struct {
Data []User `json:"data"`
Page int64 `json:"page"`
PerPage int64 `json:"perPage"`
Total int64 `json:"total"`
TotalPages int64 `json:"totalPages"`
}
func paginate(collection *mongo.Collection, filter bson.M, page, perPage int64) (*PaginatedResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
total, err := collection.CountDocuments(ctx, filter)
if err != nil {
return nil, err
}
skip := (page - 1) * perPage
opts := options.Find().
SetSkip(skip).
SetLimit(perPage).
SetSort(bson.D{{Key: "createdAt", Value: -1}})
cursor, err := collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
var users []User
if err := cursor.All(ctx, &users); err != nil {
return nil, err
}
totalPages := (total + perPage - 1) / perPage
return &PaginatedResult{
Data: users,
Page: page,
PerPage: perPage,
Total: total,
TotalPages: totalPages,
}, nil
}
Putting It All Together
func main() {
db := connectDB()
defer disconnectDB()
users := db.Collection("users")
insertUser(users)
findUsers(users)
updateUsers(users)
aggregate(users)
// Paginate
result, err := paginate(users, bson.M{"isActive": true}, 1, 10)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Page %d of %d (Total: %d)\n",
result.Page, result.TotalPages, result.Total)
}
Series Complete! 🎉
Congratulations! You've completed the entire MongoDB Tutorial series. You now know how to install MongoDB, perform CRUD operations, build complex queries, use aggregation pipelines, manage indexes, handle backups, and integrate MongoDB with Node.js, PHP, and Go. You have everything you need to build production-ready applications with MongoDB.