Skip to content

Quick Start

In this 5-minute tutorial, we'll build a simple Go application that connects to MongoDB, defines a user and a post model, and demonstrates basic CRUD operations using Goose.

1. Connect to MongoDB

Use the goose.Connect function to establish a connection. Goose provides functional options to configure your client.

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/devsamahd/goose"
)

func main() {
    ctx := context.Background()

    // Connect to MongoDB
    client, err := goose.Connect(ctx, "mongodb://localhost:27017", goose.WithDatabase("goose_quickstart"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctx)

    fmt.Println("Connected to MongoDB!")
}

Context First

Notice that ctx context.Context is passed as the first argument. Goose embraces idiomatic Go by putting context first for all I/O operations.

2. Define Your Structs

Let's define a User and a Post document. Use bson tags for MongoDB mapping and goose tags for validation and relationships.

import (
    "time"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

type User struct {
    ID        primitive.ObjectID `bson:"_id,omitempty"`
    Name      string             `bson:"name" goose:"required"`
    Email     string             `bson:"email" goose:"required,unique"`
    Age       int                `bson:"age" goose:"min=18"`
    Posts     []primitive.ObjectID `bson:"posts" goose:"ref=Post"`
    CreatedAt time.Time          `bson:"createdAt"`
}

type Post struct {
    ID      primitive.ObjectID `bson:"_id,omitempty"`
    Title   string             `bson:"title"`
    Content string             `bson:"content"`
    Author  primitive.ObjectID `bson:"author" goose:"ref=User"`
}

3. Create Models

Models provide the type-safe CRUD interface for your structs. We need to create a schema and bind it to a model.

// Create a schema for User
userSchema := goose.NewSchema()

// Add a hook to set CreatedAt automatically
userSchema.PreSave(func(ctx context.Context, doc interface{}) error {
    user := doc.(*User)
    if user.CreatedAt.IsZero() {
        user.CreatedAt = time.Now()
    }
    return nil
})

// Initialize Models
userModel := goose.NewModel[User](client, "users", userSchema)
postModel := goose.NewModel[Post](client, "posts", goose.NewSchema())

4. Insert Documents

Let's create a user and assign a post to them.

// Create a new User
user := &User{
    Name:  "Jane Doe",
    Email: "jane@example.com",
    Age:   28,
}

insertRes, err := userModel.Create(ctx, user)
if err != nil {
    log.Fatal(err)
}

// Create a new Post linked to the user
post := &Post{
    Title:   "Hello Goose",
    Content: "My very first post using the Goose ODM!",
    Author:  insertRes.InsertedID.(primitive.ObjectID),
}

if _, err := postModel.Create(ctx, post); err != nil {
    log.Fatal(err)
}

5. Query and Populate

One of Goose's most powerful features is chaining query builders and population (resolving references).

import "go.mongodb.org/mongo-driver/bson"

// Find a user by email and populate their posts
var foundUser User

err = userModel.FindOne(bson.M{"email": "jane@example.com"}).
    Populate("Posts").
    Exec(ctx, &foundUser)

if err != nil {
    log.Fatal(err)
}

fmt.Printf("Welcome, %s!\n", foundUser.Name)

Congratulations!

You have successfully connected to MongoDB, created schemas with hooks, and performed type-safe populated queries!

Check out the Configuration page to learn more about setting up Goose.