BSD 3-Clause License
Copyright (c) 2025, PedramKTB
See the LICENSE file in the root of this repository for the full license text.
go-gimpl (Go Generic Implementation) is a type-safe, enterprise-grade entity framework for Go that provides a clean abstraction layer for database operations. Currently supporting PostgreSQL with extensibility for other databases.
- π Type-Safe: Leverages Go generics for compile-time type safety
- π― CRUD Operations: Full Create, Read, Update, Delete, and Upsert support
- π Advanced Filtering: Expression-based queries with logical operators, conditionals, and quantifiers
- π Cursor Pagination: Efficient cursor-based pagination with bidirectional navigation
- π Transaction Support: First-class transaction handling with automatic rollback
- ποΈ Clean Architecture: Separation of concerns with interface-based design
- π Extensible: Easy to implement for different database systems
- β‘ Performance: Built on top of proven libraries (Squirrel, pgx)
go get github.com/pedramktb/go-gimplimport (
"github.com/pedramktb/go-gimpl"
pgimpl "github.com/pedramktb/go-gimpl/pg"
)
// Define your entity
type User struct {
ID string
Name string
Email string
}
// Implement the required interfaces (see Architecture section)
// Create datasources
db, _ := sql.Open("postgres", connString)
userFinder := pgimpl.Finder[User](db, "users")
userCreator := pgimpl.Creator[User](db, "users")
// Use the datasources
ctx := context.Background()
user, err := userFinder.FindOne(ctx, filter, nil)go-gimpl follows a clean architecture pattern with clear separation between domain models (entities) and data access operations (datasources).
Entities are domain models that implement specific interfaces depending on the operations they support. All entity methods must use value receivers (not pointer receivers).
Base Entity Interface:
type Entity interface {
// Returns pointer to field for filtering operations
FilterPtr(field string) any
// Returns pointer to field for sorting operations
SortPtr(field string) any
}Operation-Specific Entities:
CreateEntity- For creation operationsUpdateEntity- For update operationsSaveEntity- For upsert (create or update) operations
PostgreSQL-Specific Entity Extensions:
type Entity interface {
gimpl.Entity
PgColumn(field string) string // Maps field to column name
PgColumns() []string // Returns all columns for SELECT
NewWithPgColumnPtrs() (any, []any) // Creates instance with scan pointers
}
type CreateEntity interface {
gimpl.CreateEntity
CreatePgColumns() []string // Columns for INSERT
CreatePgColumnVals() []any // Values for INSERT
}
type UpdateEntity interface {
gimpl.UpdateEntity
IdentifyPgColumns() []string // Columns to identify record (e.g., ID)
IdentifyPgColumnVals() []any // Values to identify record
UpdatePgColumns() []string // Columns to update
UpdatePgColumnVals() []any // Values to update
}Datasources provide type-safe CRUD operations through specialized interfaces:
-
Finder[E Entity]- Query operationsFindOne(ctx, filter, txOpts)- Find single entityFind(ctx, filter, paginateOpts, txOpts)- Find multiple with pagination
-
Creator[C CreateEntity]- Insert operationsCreate(ctx, items, txOpts)- Insert records
-
Updater[U UpdateEntity]- Update operationsUpdate(ctx, items, txOpts)- Update records
-
Saver[S SaveEntity]- Upsert operationsSave(ctx, items, txOpts)- Insert or update records
-
Remover[E Entity]- Delete operationsRemoveOne(ctx, filter, txOpts)- Delete single recordRemove(ctx, filter, txOpts)- Delete multiple records
Creating Datasources (PostgreSQL):
db, _ := sql.Open("postgres", connString)
finder := pgimpl.Finder[User](db, "users")
creator := pgimpl.Creator[User](db, "users")
updater := pgimpl.Updater[User](db, "users")
saver := pgimpl.Saver[User](db, "users")
remover := pgimpl.Remover[User](db, "users")go-gimpl provides a powerful expression system for filtering with full type safety through JSON unmarshaling.
Expression Types:
-
Conditional Expression (
CondExpr) - Field comparisons{"field": "age", "op": "gte", "val": 18} {"field": "email", "op": "re", "val": ".*@example.com"} {"field": "status", "op": "in", "val": ["active", "pending"]} -
Logical Expression (
LogExpr) - Combine expressions{ "op": "and", "left": {"field": "age", "op": "gte", "val": 18}, "right": {"field": "status", "op": "eq", "val": "active"} } -
Quantifier Expression (
QuantExpr) - Array operations{"field": "tags", "op": "any", "expr": {"field": "name", "op": "eq", "val": "urgent"}} {"field": "scores", "op": "all", "expr": {"field": "value", "op": "gte", "val": 80}}
Operators:
- Conditional:
eq,ne,gt,lt,gte,lte,in,nin,re(regex) - Logical:
and,or - Quantifiers:
any(EXISTS),all(FOR ALL)
Usage Example:
filterJSON := `{"field": "status", "op": "eq", "val": "active"}`
var filter gimpl.Expr
filter.Sample = User{} // Required for type inference
json.Unmarshal([]byte(filterJSON), &filter)
user, err := finder.FindOne(ctx, filter, nil)Cursor-based pagination with bidirectional navigation and custom sorting.
Components:
Sorts- Sorting configuration with cursor supportCursor- Opaque cursor for pagination positionPaginated[E]- Result container with items and metadataPaginationMeta- Contains total count, next/prev cursors
Usage Example:
// Define sorting
sorts := gimpl.Sorts{Sample: User{}}
sorts.FromStr([]string{"created_at:desc", "name:asc"}, "")
// Paginate
paginated, err := finder.Find(ctx, filter,
[]gimpl.PaginateOpt{
gimpl.WithLimit(20),
gimpl.WithSorts[User](sorts),
}, nil)
// Access results
for _, user := range paginated.Items {
// Process user
}
// Navigate pages
nextCursor := paginated.Meta.Next
prevCursor := paginated.Meta.PrevFirst-class transaction support with automatic rollback and manual control.
Automatic Transaction (recommended for single operations):
// Automatically creates, commits, or rolls back
err := creator.Create(ctx, []User{user}, nil)Manual Transaction (for multiple operations):
tx, _ := pgimpl.NewTx(ctx, db)
txOpt := pgimpl.WithTx(tx)
// Multiple operations in same transaction
err := creator.Create(ctx, users, txOpt)
if err != nil {
tx.Finalize(err) // Rolls back
return err
}
err = updater.Update(ctx, updates, txOpt)
if err != nil {
tx.Finalize(err) // Rolls back
return err
}
tx.Finalize(nil) // CommitsTransaction Options:
gimpl.WithTxOpts(...)- Pass database-specific transaction optionspgimpl.WithTx(tx)- Use existing transactionpgimpl.WithTxAutoFinalize()- Auto-finalize in nested operations
- Type Safety: Leverages Go generics for compile-time guarantees
- Interface Segregation: Small, focused interfaces (Finder, Creator, etc.)
- Dependency Inversion: Depend on abstractions (interfaces), not implementations
- Immutability: Value receivers prevent accidental mutations
- Extensibility: Database-agnostic core with adapter pattern for specific databases
Join our vibrant Discord community to connect with other go-gimpl users and contributors! Whether you're just getting started or you're a seasoned contributor, our Discord server is the perfect place to:
- π¬ Ask Questions: Get help from maintainers and experienced community members
- π Share Your Projects: Show off what you're building with go-gimpl
- π Report Issues: Quick feedback and troubleshooting assistance
- π‘ Suggest Features: Share your ideas and influence the roadmap
- π€ Collaborate: Find contributors for your projects or contribute to others
- π Learn Best Practices: Exchange tips, patterns, and architectural insights
- π Stay Updated: Be the first to know about new releases and features
We're excited to have you as part of our growing community!
Thank you for contributing to the Gimpl library!
All material is licensed under the BSD 3-Clause License.
