# README
MongoDB Client Library
A production-ready MongoDB client wrapper for Go applications with built-in support for connection pooling, transactions, telemetry, and testing.
Table of Contents
- Features
- Installation
- Quick Start
- Configuration
- Working with Collections
- Transactions
- Error Handling
- Telemetry & Monitoring
- Testing
- Best Practices
- Migration Guide
- Contributing
Features
Core Features
- ๐ Automatic connection management and pooling
- ๐ Comprehensive transaction support
- ๐ Built-in telemetry and monitoring
- ๐ Configurable retry mechanisms
- โฑ๏ธ Query timeout handling
- ๐งช In-memory testing capabilities
- ๐ Detailed logging and debugging
- ๐ก๏ธ Connection security options
Performance Features
- Connection pooling optimization
- Automatic retry with exponential backoff
- Query timeout management
- Efficient resource cleanup
Installation
go get -u github.com/SolomonAIEngineering/backend-core-library/database/mongo
Quick Start
package main
import (
"context"
"time"
"github.com/SolomonAIEngineering/backend-core-library/database/mongo"
"github.com/SolomonAIEngineering/backend-core-library/instrumentation"
"go.uber.org/zap"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// Initialize client
client, err := mongo.New(
mongo.WithDatabaseName("myapp"),
mongo.WithConnectionURI("mongodb://localhost:27017"),
mongo.WithQueryTimeout(30 * time.Second),
mongo.WithLogger(zap.L()),
)
if err != nil {
panic(err)
}
defer client.Close()
// Basic CRUD operations
collection, err := client.GetCollection("users")
if err != nil {
panic(err)
}
// Insert a document
ctx := context.Background()
user := map[string]interface{}{
"name": "John Doe",
"email": "[email protected]",
"created_at": time.Now(),
}
result, err := collection.InsertOne(ctx, user)
if err != nil {
panic(err)
}
}
Configuration
Available Options
type Config struct {
// Client configuration
opts := []mongo.Option{
// Database Configuration
mongo.WithDatabaseName("mydb"),
mongo.WithConnectionURI("mongodb://localhost:27017"),
// Timeout Settings
mongo.WithQueryTimeout(60 * time.Second),
mongo.WithRetryTimeOut(30 * time.Second),
// Retry Configuration
mongo.WithMaxConnectionAttempts(3),
mongo.WithMaxRetriesPerOperation(3),
mongo.WithOperationSleepInterval(5 * time.Second),
// Monitoring & Logging
mongo.WithTelemetry(&instrumentation.Client{}),
mongo.WithLogger(zap.L()),
// Collection Management
mongo.WithCollectionNames([]string{"users", "products", "orders"}),
// Advanced Options
mongo.WithClientOptions(options.Client().
ApplyURI("mongodb://localhost:27017").
SetMaxPoolSize(100).
SetMinPoolSize(10)),
}
}
Security Configuration
// TLS Configuration
tlsConfig := &tls.Config{
// ... your TLS configuration
}
opts := []mongo.Option{
mongo.WithClientOptions(options.Client().
ApplyURI("mongodb://localhost:27017").
SetTLSConfig(tlsConfig)),
mongo.WithCredentials(options.Credential{
Username: "user",
Password: "pass",
AuthSource: "admin",
}),
}
Working with Collections
Basic CRUD Operations
// Get collection
collection, err := client.GetCollection("users")
if err != nil {
return err
}
// Insert
doc := map[string]interface{}{
"name": "Alice",
"age": 30,
}
result, err := collection.InsertOne(ctx, doc)
// Find
var user map[string]interface{}
err = collection.FindOne(ctx, bson.M{"name": "Alice"}).Decode(&user)
// Update
update := bson.M{"$set": bson.M{"age": 31}}
_, err = collection.UpdateOne(
ctx,
bson.M{"name": "Alice"},
update,
)
// Delete
_, err = collection.DeleteOne(ctx, bson.M{"name": "Alice"})
Bulk Operations
// Bulk Insert
documents := []interface{}{
bson.D{{"name", "User1"}, {"age", 25}},
bson.D{{"name", "User2"}, {"age", 30}},
}
result, err := collection.InsertMany(ctx, documents)
// Bulk Update
updates := []mongo.WriteModel{
mongo.NewUpdateOneModel().
SetFilter(bson.M{"name": "User1"}).
SetUpdate(bson.M{"$set": bson.M{"age": 26}}),
mongo.NewUpdateOneModel().
SetFilter(bson.M{"name": "User2"}).
SetUpdate(bson.M{"$set": bson.M{"age": 31}}),
}
_, err = collection.BulkWrite(ctx, updates)
Transactions
Standard Transaction Example
err := client.StandardTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
// Get collections
users, err := client.GetCollection("users")
if err != nil {
return nil, err
}
orders, err := client.GetCollection("orders")
if err != nil {
return nil, err
}
// Perform operations
_, err = users.InsertOne(sessCtx, userDoc)
if err != nil {
return nil, err
}
_, err = orders.InsertOne(sessCtx, orderDoc)
if err != nil {
return nil, err
}
return nil, nil
})
Complex Transaction with Return Value
type TransactionResult struct {
UserID string
OrderID string
}
result, err := client.ComplexTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
// Your transaction logic here
userResult, err := users.InsertOne(sessCtx, userDoc)
if err != nil {
return nil, err
}
orderResult, err := orders.InsertOne(sessCtx, orderDoc)
if err != nil {
return nil, err
}
return &TransactionResult{
UserID: userResult.InsertedID.(string),
OrderID: orderResult.InsertedID.(string),
}, nil
})
Error Handling
// Custom error types
var (
ErrConnectionFailed = errors.New("failed to connect to mongodb")
ErrQueryTimeout = errors.New("query timeout")
)
// Error handling example
collection, err := client.GetCollection("users")
if err != nil {
switch {
case errors.Is(err, mongo.ErrCollectionNotFound):
// Handle collection not found
case errors.Is(err, mongo.ErrDatabaseNotFound):
// Handle database not found
default:
// Handle other errors
}
return err
}
Telemetry & Monitoring
// Initialize with telemetry
telemetryClient := &instrumentation.Client{
// Configure your telemetry client
}
client, err := mongo.New(
mongo.WithTelemetry(telemetryClient),
mongo.WithMetricsEnabled(true),
)
// Access metrics
metrics := client.GetMetrics()
fmt.Printf("Total Queries: %d\n", metrics.TotalQueries)
fmt.Printf("Failed Queries: %d\n", metrics.FailedQueries)
fmt.Printf("Average Query Time: %v\n", metrics.AverageQueryTime)
Testing
In-Memory Testing
func TestUserRepository(t *testing.T) {
// Create test client
testClient, err := mongo.NewInMemoryTestDbClient(
[]string{"users", "orders"},
)
if err != nil {
t.Fatal(err)
}
defer testClient.Teardown()
// Run tests
t.Run("InsertUser", func(t *testing.T) {
collection, err := testClient.GetCollection("users")
if err != nil {
t.Fatal(err)
}
user := bson.M{"name": "Test User"}
_, err = collection.InsertOne(context.Background(), user)
assert.NoError(t, err)
})
}
Mocking
type MockMongoClient struct {
mongo.Client
MockInsertOne func(context.Context, interface{}) (*mongo.InsertOneResult, error)
}
func (m *MockMongoClient) InsertOne(ctx context.Context, document interface{}) (*mongo.InsertOneResult, error) {
return m.MockInsertOne(ctx, document)
}
Best Practices
Connection Management
// Initialize once at application startup
client, err := mongo.New(opts...)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Reuse the client throughout your application
Query Optimization
// Use appropriate indexes
collection.CreateIndex(ctx, mongo.IndexModel{
Keys: bson.D{{"field", 1}},
Options: options.Index().SetUnique(true),
})
// Use projection to limit returned fields
collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{
"needed_field": 1,
"_id": 0,
}))
Resource Management
// Use context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Use cursor properly
cursor, err := collection.Find(ctx, filter)
if err != nil {
return err
}
defer cursor.Close(ctx)
Migration Guide
Upgrading from v1.x to v2.x
// Old v1.x configuration
oldClient := mongo.NewClient(
"mongodb://localhost:27017",
"mydb",
)
// New v2.x configuration
newClient, err := mongo.New(
mongo.WithConnectionURI("mongodb://localhost:27017"),
mongo.WithDatabaseName("mydb"),
)
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/SolomonAIEngineering/backend-core-library.git
# Install dependencies
go mod download
# Run tests
go test -v ./...
# Run linting
golangci-lint run
License
This MongoDB client is released under the MIT License. See LICENSE for details.
Support
- ๐ Documentation
- ๐ฌ GitHub Discussions
- ๐ Issue Tracker
# Functions
New creates a new instance of the mongo client.
NewInMemoryTestDbClient creates a new in-memory MongoDB database and returns a client to it.
ObjectIDFromHexIDString takes a pointer to a string as input and returns a primitive.ObjectID and an error.
WithClientOptions sets the client options.
WithCollectionNames sets the collection names.
WithConnectionURI sets the connection URI.
WithDatabaseName sets the database name.
WithLogger sets the logging utility used by this object.
WithMaxConnectionAttempts sets the maximum connection attempts to initiate against the database.
WithMaxRetriesPerOperation sets the maximum retries to attempt per failed database connection attempt.
WithOperationSleepInterval sets the amount of time between retry operations that the system sleeps.
WithQueryTimeout sets the maximal amount of time a query can execute before being cancelled.
WithRetryTimeOut sets the maximum time until a retry operation is observed as a timed out operation.
WithTelemetry sets the object by which we will emit metrics, trace requests, and database operations.
# Structs
No description provided by the author
No description provided by the author
# Interfaces
No description provided by the author