DEV Community

MongoDB Guests for MongoDB

Posted on

7 1 1 1 1

Skip the ORM/ODM: Why MongoDB’s Native Go Driver is the Best Choice

This tutorial was written by Julia Tazin.

When working with MongoDB in Go, a common question is whether to use an ORM or ODM—or forgo them entirely. While ORMs and ODMs can offer convenience and structure, Go’s core philosophy favors simplicity and directness. Instead of adding an extra abstraction layer, the official MongoDB Go Driver provides the functionality and flexibility you need to interact with MongoDB in a natural, efficient way.

In this tutorial, we’ll explore what ORMs and ODMs are, how they differ, and why many Go developers prefer the native MongoDB Go Driver over these abstractions.

What is an ORM? What is an ODM?

An object-relational mapper (ORM) is a programming tool that lets you interact with a relational database using objects from your language. Instead of writing raw SQL queries, you define structs or classes that map to database tables.

An object-document mapper (ODM) serves a similar purpose but is designed for NoSQL databases like MongoDB. Instead of mapping structs to tables, an ODM maps Go structs to BSON documents, enabling you to work with flexible, schema-less data while abstracting low-level operations like querying and indexing.

ORM: Struct ↔ Table (SQL)
ODM: Struct ↔ Document (NoSQL)

Why is the official MongoDB Go Driver the best option?

The official MongoDB Go Driver is fast, stable, and idiomatic. It offers direct access to all of MongoDB’s features without forcing any conventions or magic behind the scenes. Here's why it’s often the best choice for Go developers:

✅ Full control

The driver gives you complete freedom to write your own queries, manage indexes, and handle complex document structures. You’re not constrained by an abstraction layer that might oversimplify important operations.

✅ Idiomatic Go

Go favors simplicity and clarity. The official driver lets you work directly with structs and native types, keeping everything explicit and familiar. You don’t have to learn a new DSL or annotation-heavy model layer.

✅ Actively maintained

It’s backed and maintained by MongoDB, ensuring it stays up to date with new MongoDB features, performance improvements, and security patches.

✅ No hidden performance costs

Abstractions can come with a cost—especially in performance-critical systems. Using the driver directly avoids unnecessary overhead and gives you full visibility into what’s happening with your database calls.

✅ All you need, without the extra layer

The MongoDB Go Driver already gives you what most ODMs offer—struct mapping, typed filters, and support for nested documents—without introducing another abstraction. It keeps things simple and Go-idiomatic, just as it should be.

Get started with the MongoDB Go Driver

Ready to get started? The Quick Start guide gets you up and running. And if you're curious about the latest improvements, the What's New page keeps you in the loop.

Want to see a more detailed example? Try out the following updated version of the MongoDB Go Driver Tutorial, which has been adapted for the 2.0 release of the Go driver.

Step 1: Create a go.mod file with the following contents.

go mod init example.com/mongodb_tutorial
go get go.mongodb.org/mongo-driver/v2
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a main.go file that will contain our code.

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    "go.mongodb.org/mongo-driver/v2/bson"
    "go.mongodb.org/mongo-driver/v2/mongo"
    "go.mongodb.org/mongo-driver/v2/mongo/options"
    "go.mongodb.org/mongo-driver/v2/mongo/readpref"
)

type Trainer struct {
    Name string `bson:"name"`
    Age  int    `bson:"age"`
    City string `bson:"city"`
}


func main() {
    uri := os.Getenv("MONGODB_URI")
    if uri == "" {
        log.Fatal("Environment variable MONGODB_URI must be set")
    }

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(options.Client().ApplyURI(uri))
    if err != nil {
        log.Fatal("Error connecting to MongoDB:", err)
    }
    defer func() {
        if err = client.Disconnect(ctx); err != nil {
            log.Fatal("Error disconnecting from MongoDB:", err)
        }
    }()

    if err := client.Ping(ctx, readpref.Primary()); err != nil {
        log.Fatal("Could not ping MongoDB:", err)
    }
    fmt.Println("Connected to MongoDB!")

    collection := client.Database("test").Collection("trainers")

    ash := Trainer{Name: "Ash", Age: 10, City: "Pallet Town"}
    misty := Trainer{Name: "Misty", Age: 10, City: "Cerulean City"}
    brock := Trainer{Name: "Brock", Age: 15, City: "Pewter City"}

    trainers := []interface{}{ash, misty, brock}
    insertResult, err := collection.InsertMany(ctx, trainers)
    if err != nil {
        log.Fatal("InsertMany failed:", err)
    }
    fmt.Println("Inserted documents:", insertResult.InsertedIDs)

    filter := bson.D{{Key: "name", Value: "Ash"}}
    var result Trainer
    err = collection.FindOne(ctx, filter).Decode(&result)
    if err != nil {
        log.Fatal("FindOne failed:", err)
    }
    fmt.Printf("Found document: %+v\n", result)

    cursor, err := collection.Find(ctx, bson.D{})
    if err != nil {
        log.Fatal("Find failed:", err)
    }
    defer cursor.Close(ctx)

    fmt.Println("All trainers:")
    for cursor.Next(ctx) {
        var t Trainer
        if err := cursor.Decode(&t); err != nil {
            log.Fatal("Cursor decode failed:", err)
        }
        fmt.Printf(" - %+v\n", t)
    }
    if err := cursor.Err(); err != nil {
        log.Fatal("Cursor error:", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Clean up your module dependencies to make sure nothing’s missing by running:

go mod tidy
Enter fullscreen mode Exit fullscreen mode

Step 4: Finally, run the script with a MongoDB connection string as an environment variable—for example:

MONGODB_URI=mongodb://localhost:27017 go run main.go
Enter fullscreen mode Exit fullscreen mode

This example will connect to the specified cluster, insert some documents, try and retrieve a newly inserted document, then retrieve all documents.

Connected to MongoDB!
Inserted documents: [ObjectID("68222e79d2cdea1807c70cb5") ObjectID("68222e79d2cdea1807c70cb6") ObjectID("68222e79d2cdea1807c70cb7")]
Found document: {Name:Ash Age:10 City:Pallet Town}
All trainers:
 - {Name:Ash Age:10 City:Pallet Town}
 - {Name:Misty Age:10 City:Cerulean City}
 - {Name:Brock Age:15 City:Pewter City}
Enter fullscreen mode Exit fullscreen mode

Feel free to kick the tires yourself and adapt the code to see how easy working with MongoDB from your Go application can be. With solid documentation, a vibrant community, and first-class support, there's no better way to integrate MongoDB into your Go project.

Conclusion

While ORMs and ODMs have their place, Go's native approach to data modeling, combined with MongoDB’s document model, means you often don’t need an extra abstraction. If you value performance, clarity, and full control, the official MongoDB Go Driver is the way to go.

AWS Q Developer image

What is MCP? No, Really!

See MCP in action and explore how MCP decouples agents from servers, allowing for seamless integration with cloud-based resources and remote functionality.

Watch the demo

Top comments (1)

Collapse
 
stevsharp profile image
Spyros Ponaris

If MongoDB is the only solution, then going native might be the right call.
But if you ever need to switch databases or use different databases depending on the scenario, then an ORM is definitely the better choice for flexibility and abstraction.

Embedded BI Dashboards are 💩 Building them yourself is 💩

Embedded BI Dashboards are 💩 Building them yourself is 💩

Use our developer toolkit to build fast-loading, native-feeling dashboards for your customers (without the sh*t).

Get early access

👋 Kindness is contagious

Delve into this thought-provoking piece, celebrated by the DEV Community. Coders from every walk are invited to share their insights and strengthen our collective intelligence.

A heartfelt “thank you” can transform someone’s day—leave yours in the comments!

On DEV, knowledge sharing paves our journey and forges strong connections. Found this helpful? A simple thanks to the author means so much.

Get Started