mongox is a high-level MongoDB interface for Go that simplifies database operations and provides rich error handling. It wraps the official MongoDB Go driver to reduce boilerplate code and make MongoDB operations more intuitive.
Whether you are building small applications or large-scale systems, mongox streamlines your database operations, allowing you to focus on what truly matters—developing your application.
- Simplified Interface: Reduce boilerplate code with an intuitive API.
- Rich Error Handling: Error types for all possible error codes.
- Async Operations: Fire-and-forget operations with error handling, statistics, and retry monitoring.
- Concurrent Safety: Designed for safe use across multiple goroutines.
- Well tested code: Built on top of official MongoDB Go driver,
mongoxhas 87% test coverage with integration tests using real MongoDB instance.
go get -u github.com/maxbolgarin/mongoxBefore performing any operations, initialize a Collection instance:
cfg := mongox.Config{
AppName: "MyApp",
Address: "localhost:27017",
Auth: &mongox.AuthConfig{
Username: "username",
Password: "password",
},
// TODO: other settings
}
client, err := mongox.Connect(ctx, cfg)
if err != nil {
return err
}
defer client.Disconnect(ctx) // TODO: handle error
db := client.Database("mydb")
collection := db.Collection("users")Insert single or multiple documents into the collection:
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
}user := User{Name: "Alice", Age: 30}
// Insert a single document
_, err := collection.Insert(ctx, user)
if err != nil {
return err
}
// Insert multiple documents
_, err = collection.Insert(ctx,
User{Name: "Bob", Age: 30},
User{Name: "Charlie", Age: 35},
)
if err != nil {
return err
}
// Insert with generic method
_, err = mongox.Insert(ctx, collection, User{Name: "Mike", Age: 20})
if err != nil {
return err
}Get documents from the collection:
// Define a filter
filter := mongox.M{"age": mongox.Gt(20)}
// Find multiple documents
var users []mongox.User
err = collection.Find(ctx, &users, filter)
if err != nil {
return err
}
// Find a one document with a generic method and applied sort
result, err := mongox.FindOne[User](ctx, collection, filter, mongox.FindOptions{
Sort: mongox.M{"age": 1},
})
if err != nil {
return err
}Modify existing documents in the collection. You can redefine mongox.M to make code cleaner:
type M = mongox.Mupdate := M{
mongox.Inc: M{
"age": 1,
},
}
// Update a single document
err := collection.UpdateOne(ctx, filter, update)
if err != nil {
return err
}
// Set new fields
err := collection.SetFields(ctx, filter, M{"new_field": "value"})
if err != nil {
return err
}
// Upsert a document (update or insert)
record := User{
Name: "Diana",
Age: 28,
}
_, err = collection.Upsert(ctx, record, M{"name": "Alice"})
if err != nil {
return err
}Remove documents from the collection:
// Delete a single document
err := collection.DeleteOne(ctx, M{"name": "Diana"})
if err != nil {
return err
}
// Delete multiple documents
n, err = collection.DeleteMany(ctx, M{"age": mongox.Lt(30)})
if err != nil {
return err
}Create indexes to optimize query performance.
// Create a unique index on the 'email' field
err := collection.CreateIndex(ctx, true, "email")
if err != nil {
return err
}
// Create a text index for text search on 'name' and 'bio' fields
err = collection.CreateTextIndex(ctx, "en", "name", "bio")
if err != nil {
return err
}mongox provides error handling:
err := collection.Insert(ctx, User{
Name: eveName,
Age: eveAge,
})
if err != nil {
if errors.Is(err, mongox.ErrNotFound) {
// Handle not found
} else if errors.Is(err, mongox.ErrInvalidArgument) {
// Handle invalid argument
} else {
// Handle other errors
}
}mongox supports asynchronous operations using AsyncCollection:
asyncDB := client.AsyncDatabase(ctx, "mydb", 10, logger) // 10 workers
asyncCollection := asyncDB.AsyncCollection("users")
// Insert a document asynchronously
asyncCollection.Insert("users_queue", "insert_task", User{
Name: "Alice",
Age: 30,
})- Operations with the same queue name (first argument,
users_queuein example) will be executed sequentially in strict order of calling - Operations with different queue names will be executed in parallel
- Task name (second argument) is used for logging
Handle errors from async operations programmatically:
asyncDB := client.AsyncDatabase(ctx, "mydb", 10, logger)
// Set up error handler to receive all async errors
asyncDB.WithErrorHandler(func(err *mongox.AsyncError) {
log.Printf("Async error in %s.%s: %v", err.Collection, err.Operation, err.Err)
// Check error type
if errors.Is(err.Err, mongox.ErrDuplicate) {
// Handle duplicate key error
alerting.Send("Duplicate key in " + err.Collection)
}
// IsNotRetryable=true means error won't be retried (e.g., ErrNotFound, ErrDuplicate)
// IsNotRetryable=false means task exhausted all retries
if !err.IsNotRetryable {
log.Printf("Task %s exhausted all %d retries", err.TaskName, err.RetryCount)
}
})Monitor async error statistics:
// Get current statistics snapshot
stats := asyncDB.ErrorStats()
fmt.Printf("Total errors: %d\n", stats.TotalErrors)
fmt.Printf("Non-retryable: %d\n", stats.NonRetryableErrors)
fmt.Printf("Retry-exhausted: %d\n", stats.RetryExhaustedErrors)
// Statistics by collection
for coll, s := range stats.ByCollection {
fmt.Printf("Collection %s: %d errors\n", coll, s.Total)
}
// Statistics by operation
for op, s := range stats.ByOperation {
fmt.Printf("Operation %s: %d errors\n", op, s.Total)
}
// Top 10 most frequent errors
for _, e := range stats.TopErrors {
fmt.Printf("Error '%s': %d occurrences\n", e.Error, e.Count)
}
// Reset statistics
asyncDB.ResetErrorStats()Monitor tasks that have exhausted all retry attempts:
// Start background monitoring for retry-exhausted errors
asyncDB.StartRetryExhaustedMonitor(ctx, 5*time.Second)
// The monitor will call error handler when tasks exhaust retries
// Cancel context to stop the monitorIf you don't need error statistics:
asyncDB := client.AsyncDatabase(ctx, "mydb", 10, logger).WithNoAsyncStats()Contributions are welcome! Please feel free to submit a Pull Request or an Issue.
This project is licensed under the terms of the MIT License.