NoSQL / MongoDB
Nimbus ships a first-class NoSQL layer with a unified Driver / Collection interface and a production-ready MongoDB implementation. The API keeps the same feel as the SQL side โ register a connection, grab it by name, and use a fluent query builder or the raw Collection methods.
Documentation
Installation
The fastest way to get started is with the CLI plugin command. It scaffolds the config, boot wiring, and environment variables for you:
nimbus plugin:install nosql
This single command:
- Creates
config/nosql.gowith aNoSQLConfigstruct - Adds a
bootNoSQL(app)function inbin/server.gothat connects MongoDB, registers the driver globally, and binds*nimbus.NoSQLinto the service container - Appends
MONGO_URIandMONGO_DATABASEto.env.example - Registers
loadNoSQL()in yourconfig/config.go
Manual Installation
If you prefer manual setup, pull in the NoSQL package and the MongoDB driver:
go get github.com/CodeSyncr/nimbus/database/nosql
go get go.mongodb.org/mongo-driver/v2
Configuration
Add MONGO_URI and MONGO_DATABASE to your .env:
MONGO_URI=mongodb://localhost:27017
MONGO_DATABASE=myapp
Scaffolded Config (plugin:install)
Running nimbus plugin:install nosql creates config/nosql.go with a dedicated config struct:
package config
var NoSQL NoSQLConfig
type NoSQLConfig struct {
MongoURI string
MongoDatabase string
ConnectTimeout string // default: "10s"
MaxPoolSize uint64 // default: 100
MinPoolSize uint64 // default: 0
}
func loadNoSQL() {
NoSQL = NoSQLConfig{
MongoURI: cfg("mongo.uri", ""),
MongoDatabase: cfg("mongo.database", "nimbus"),
ConnectTimeout: cfg("mongo.connect_timeout", "10s"),
}
}
The generated bootNoSQL function in bin/server.go wires everything automatically:
func bootNoSQL(app *nimbus.App) {
if config.NoSQL.MongoURI == "" {
return // NoSQL not configured โ skip silently
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
mongoDriver, err := nosql.ConnectMongo(ctx, nosql.MongoConfig{
URI: config.NoSQL.MongoURI,
Database: config.NoSQL.MongoDatabase,
})
if err != nil {
fmt.Fprintf(os.Stderr, "MongoDB connection failed: %v\n", err)
os.Exit(1)
}
// Register in the NoSQL connection manager
nosql.Register("mongo", mongoDriver)
// Set the global *nimbus.NoSQL handle
nimbus.SetNoSQL(mongoDriver)
// Bind into the service container for DI
app.Container.Singleton("nosql", func() *nimbus.NoSQL {
return nimbus.GetNoSQL()
})
}
Manual Boot (without plugin:install)
If you set up manually, connect in bin/server.go or your boot file:
import "github.com/CodeSyncr/nimbus/database/nosql"
mongoDriver, err := nosql.ConnectMongo(ctx, nosql.MongoConfig{
URI: config.Database.MongoURI,
Database: config.Database.MongoDatabase,
})
if err != nil {
log.Fatalf("mongo: %v", err)
}
nosql.Register("mongo", mongoDriver)
nimbus.SetNoSQL(mongoDriver) // enable *nimbus.NoSQL app-level access
| MongoConfig field | Type | Default | Description |
|---|---|---|---|
URI | string | โ | MongoDB connection string |
Database | string | โ | Default database name |
ConnectTimeout | time.Duration | 10s | Connection timeout |
MaxPoolSize | uint64 | 100 | Max connections in pool |
MinPoolSize | uint64 | 0 | Min idle connections |
Using the Connection
Application-Level Access (*nimbus.NoSQL)
Just like *nimbus.DB for SQL, the framework provides *nimbus.NoSQL as a high-level handle. Use the global helper or resolve from the container:
// Global helper โ works anywhere
store := nimbus.GetNoSQL() // *nimbus.NoSQL
coll := store.Collection("users") // nosql.Collection
// Shorthand โ collection from the default connection
coll = nimbus.NoSQLCollection("users")
// Named connection (multi-db)
analytics := nimbus.NoSQLConnection("analytics") // *nimbus.NoSQL
Container Injection (Dependency Injection)
When bootNoSQL runs, it binds *nimbus.NoSQL into the service container. Resolve it in controllers or services:
type TodoController struct {
Store *nimbus.NoSQL
}
func NewTodoController(app *nimbus.App) *TodoController {
return &TodoController{
Store: app.Container.MustMake("nosql").(*nimbus.NoSQL),
}
}
func (tc *TodoController) Index(c *nimbus.Context) error {
var todos []Todo
tc.Store.Collection("todos").Find(c.Request.Context(), nosql.Filter{"active": true}, &todos)
return c.JSON(todos)
}
Low-Level Package Access
You can also use the nosql package directly:
store := nosql.Connection("mongo") // returns nosql.Driver
coll := store.Collection("users") // returns nosql.Collection
// Switch database at runtime
analytics := store.Database("analytics") // same client, different DB
events := analytics.Collection("events")
CRUD Operations
Insert
// Model definition โ embed nosql.Model just like database.Model for SQL
type User struct {
nosql.Model
Name string
Email string
Age int
}
coll := nimbus.NoSQLCollection("users")
// Insert one
res, err := coll.InsertOne(ctx, User{Name: "Alice", Email: "alice@example.com", Age: 28})
fmt.Println(res.InsertedID)
// Insert many
bulk, err := coll.InsertMany(ctx, []any{
User{Name: "Bob", Email: "bob@example.com", Age: 32},
User{Name: "Carol", Email: "carol@example.com", Age: 24},
})
Find
// Find one
var user User
err := coll.FindOne(ctx, nosql.Filter{"email": "alice@example.com"}, &user)
// Find by ID
err = coll.FindByID(ctx, objectID, &user)
// Find multiple with options
var users []User
err = coll.Find(ctx, nosql.Filter{"age": nosql.Document{"$gte": 21}}, &users, nosql.FindOption{
Sort: nosql.Sort{"name": nosql.Ascending},
Limit: 50,
Skip: 0,
})
// Count & Exists
count, _ := coll.Count(ctx, nosql.Filter{"active": true})
exists, _ := coll.Exists(ctx, nosql.Filter{"email": "alice@example.com"})
Update
// Update one (auto-wraps in $set)
res, err := coll.UpdateOne(ctx,
nosql.Filter{"email": "alice@example.com"},
nosql.Filter{"age": 29},
)
// Update many
res, err = coll.UpdateMany(ctx,
nosql.Filter{"active": false},
nosql.Filter{"status": "archived"},
)
// Update by ID
res, err = coll.UpdateByID(ctx, objectID, nosql.Filter{"name": "Alice W."})
// Upsert (insert-or-update)
res, err = coll.Upsert(ctx,
nosql.Filter{"email": "new@example.com"},
nosql.Filter{"name": "New User", "email": "new@example.com"},
)
// Use raw MongoDB operators
res, err = coll.UpdateOne(ctx,
nosql.Filter{"_id": id},
nosql.Filter{"$inc": nosql.Document{"login_count": 1}},
)
Delete
// Delete one
res, err := coll.DeleteOne(ctx, nosql.Filter{"email": "alice@example.com"})
// Delete many
res, err = coll.DeleteMany(ctx, nosql.Filter{"status": "archived"})
// Delete by ID
res, err = coll.DeleteByID(ctx, objectID)
Aggregation
// Aggregation pipeline
pipeline := bson.A{
bson.D{{Key: "$match", Value: bson.D{{Key: "status", Value: "active"}}}},
bson.D{{Key: "$group", Value: bson.D{
{Key: "_id", Value: "$country"},
{Key: "count", Value: bson.D{{Key: "$sum", Value: 1}}},
}}},
}
var results []bson.M
err := coll.Aggregate(ctx, pipeline, &results)
// Distinct values
emails, err := coll.Distinct(ctx, "email", nosql.Filter{"active": true})
Indexing
// Simple index
name, err := coll.CreateIndex(ctx, nosql.Document{"email": 1})
// Unique index
name, err = coll.CreateIndex(ctx, nosql.Document{"email": 1}, nosql.IndexOption{
Unique: true,
Name: "idx_users_email",
})
// Compound index
name, err = coll.CreateIndex(ctx, nosql.Document{"status": 1, "created_at": -1})
// TTL index (expire docs after 24h)
ttl := int32(86400)
name, err = coll.CreateIndex(ctx, nosql.Document{"expires_at": 1}, nosql.IndexOption{
ExpireAfterSeconds: &ttl,
})
// Sparse index
name, err = coll.CreateIndex(ctx, nosql.Document{"phone": 1}, nosql.IndexOption{
Sparse: true,
})
// Drop an index
err = coll.DropIndex(ctx, "idx_users_email")
Models
Embed nosql.Model for standard fields โ the same pattern as database.Model on the SQL side:
Base Model
// nosql.Model provides the same standard fields as database.Model (SQL)
type Model struct {
ID string // document _id
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time // soft delete (nil = not deleted)
}
Defining a Model
type Post struct {
nosql.Model
Title string
Body string
Status string
}
type Comment struct {
nosql.Model
PostID string
Content string
}
This mirrors the SQL side where models embed database.Model:
| SQL (database.Model) | NoSQL (nosql.Model) |
|---|---|
ID uint | ID string (MongoDB ObjectID) |
CreatedAt time.Time | CreatedAt time.Time |
UpdatedAt time.Time | UpdatedAt time.Time |
DeletedAt gorm.DeletedAt | DeletedAt *time.Time |
Validators
Validation works exactly the same as the SQL side โ define a validator struct with Rules() and call Validate(). Place validators in app/validators/:
package validators
import "github.com/CodeSyncr/nimbus/validation"
type Post struct {
Title string
Body string
Status string
}
func (v *Post) Rules() validation.Schema {
return validation.Schema{
"title": validation.String().Required().Min(1).Max(255).Trim(),
"body": validation.String().Required().Min(1).Max(5000),
"status": validation.String().In("draft", "published", "archived"),
}
}
func (v *Post) Validate() error {
return validation.ValidateStruct(v)
}
Use the validator in your controller โ same pattern as CRUD with SQL:
func (pc *PostController) Store(ctx *http.Context) error {
_ = ctx.Request.ParseForm()
v := &validators.Post{
Title: strings.TrimSpace(ctx.Request.FormValue("title")),
Body: ctx.Request.FormValue("body"),
Status: ctx.Request.FormValue("status"),
}
if err := v.Validate(); err != nil {
return ctx.JSON(http.StatusUnprocessableEntity, map[string]any{"errors": err})
}
post := &models.Post{Title: v.Title, Body: v.Body, Status: v.Status}
_, err := pc.Store.Collection("posts").InsertOne(ctx.Request.Context(), post)
if err != nil {
return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return ctx.JSON(post)
}
Connection Lifecycle
// Health check
if err := nosql.Connection("mongo").Ping(ctx); err != nil {
log.Println("MongoDB is unreachable:", err)
}
// Close the default *nimbus.NoSQL handle
defer nimbus.CloseNoSQL(ctx)
// Or close all registered NoSQL connections
defer nosql.CloseAll(ctx)
Accessing the Raw Client
For features not exposed by the Collection interface you can reach the underlying *mongo.Client:
driver := nosql.MustConnection("mongo").(*nosql.MongoDriver)
client := driver.Client() // *mongo.Client
db := driver.DB() // *mongo.Database
Next: NoSQL Query Builder โ