Skip to content
/ gerpo Public

GERPO is a generic repository pattern implementation with language integrated query support. Works with database/sql sources.

License

Notifications You must be signed in to change notification settings

Insei/gerpo

Repository files navigation

GERPO

codecov build Goreport GoDoc

Welcome to the GERPO repository! This document provides a brief overview of the project, build and run instructions, and other helpful information.

About GERPO

GERPO (Golang + Repository) is a generic repository implementation with advanced configuration capabilities and LINQ-like (Language Integrated Query) support.

Why GERPO?

  1. Easily handle CRUD operations (Create, Read, Update, Delete) with powerful filtering and sorting.
  2. LINQ-like capabilities (while not exactly LINQ, it’s conceptually close).
  3. Straightforward configuration using SQL commands and user-friendly builders.
  4. All SQL code in one place — inside the configuration.
  5. Virtual (calculated, joined) columns with mapping to struct fields.
  6. Caching support (currently only context-oriented cache is supported, but it’s easy to implement other caching mechanisms).

Features

Essentially, GERPO is a helper for building SQL queries and mapping results to Go structs.

  • Repository configuration:

    • Map struct fields to SQL columns via a LINQ-like builder.
    • Define virtual (calculated) fields (currently supports only bool; contributions welcome).
    • Protect certain fields from being updated or inserted.
    • Add callbacks and hooks.
    • Define persistent filters, groupings, and joins.
    • Configure soft deletion
  • Per-request configuration:

    • Exclude certain columns (SELECT/INSERT/UPDATE) using a builder.
    • Work with transactions.
    • Configure filtering and sorting via a LINQ-like builder.
    • Implement pagination in your GetList requests.

Installation

go get github.com/insei/gerpo@latest

Examples

Below you’ll find various configurations and usage examples.

Repository Configuration

Columns

package main

import (
    "time"
    "github.com/insei/gerpo"
)

type test struct {
    ID        int
    CreatedAt time.Time
    UpdatedAt *time.Time
    Name      string
    Age       int
}

func main() {
    repo, err := gerpo.NewBuilder[test]().
        DB(db).
        Table("tests").
        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
            columns.Field(&m.ID).AsColumn().WithUpdateProtection()
            columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
            columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
            columns.Field(&m.Name).AsColumn()
            columns.Field(&m.Age).AsColumn()
        }).
        Build()

    // Handle err and proceed with repo usage
}

Joins

package main

import (
    "context"
    "time"
    "github.com/insei/gerpo"
    "github.com/insei/gerpo/query"
)

type test struct {
    ID        int
    CreatedAt time.Time
    UpdatedAt *time.Time
    Name      string
    Age       int
    Joined    string
}

func main() {
    repo, err := gerpo.NewBuilder[test]().
        DB(db).
        Table("tests").
        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
            columns.Field(&m.ID).AsColumn().WithUpdateProtection()
            columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
            columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
            columns.Field(&m.Name).AsColumn()
            columns.Field(&m.Age).AsColumn()
            columns.Field(&m.Joined).AsColumn().WithTable("joined_table")
        }).
        WithQuery(func(m *test, h query.PersistentHelper[test]) {
            h.LeftJoin(func(ctx context.Context) string {
                return "<SQL JOIN COMMAND>"
            })
        }).
        Build()

    // Handle err and proceed with repo usage
}

Soft Deletion

package main

import (
    "context"
    "time"
    "github.com/insei/gerpo"
    "github.com/insei/gerpo/query"
)

type test struct {
    ID        int
    CreatedAt time.Time
    UpdatedAt *time.Time
    Name      string
    Age       int
	DeletedAt *time.Time
}

func main() {
    repo, err := gerpo.NewBuilder[test]().
        DB(db).
        Table("tests").
        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
            columns.Field(&m.ID).AsColumn().WithUpdateProtection()
            columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
            columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
            columns.Field(&m.Name).AsColumn()
            columns.Field(&m.Age).AsColumn()
            columns.Field(&m.DeletedAt).AsColumn().WithInsertProtection() // configure soft deletion field/column
        }).
        WithSoftDeletion(func(m *User, softDeletion *gerpo.SoftDeletionBuilder[User]) {
            //Configure set value for soft deletion fields/columns
            softDeletion.Field(&m.DeletedAt).SetValueFn(func(ctx context.Context) any {
                deletedAt := time.Now().UTC()
                return &deletedAt
            })
        }).
        WithQuery(func(m *test, h query.PersistentHelper[test]) {
            // Permanently exclude deleted elements from all queries
            h.Where().Field(&m.DeletedAt).EQ(nil)
        }).
        Build()

    // Handle err and proceed with repo usage
}

Per-request Configuration

Exclude

Exclude certain fields from commands like SELECT/UPDATE/INSERT (Update/GetFirst/Insert/GetList):

package main

import (
    "context"
    "github.com/insei/gerpo"
    "github.com/insei/gerpo/query"
)

type test struct {
  ID        int
  CreatedAt time.Time
  UpdatedAt *time.Time
  Name      string
  Age       int
  Joined    string
}

func main() {
  var repo gerpo.Repository[test] // Already initialized
    list, err := repo.GetList(ctxCache, func(m *test, h query.GetListHelper[test]) {
        h.Page(1).Size(2) // Pagination
        h.Exclude(&m.UpdatedAt, &m.ID)
    })
    // Handle err and work with the list
}

Where

Available for Count/GetFirst/GetList/Delete/Update, supporting where grouping (AND/OR):

package main

import (
    "context"
    "github.com/insei/gerpo"
    "github.com/insei/gerpo/query"
)

type test struct {
  ID        int
  CreatedAt time.Time
  UpdatedAt *time.Time
  Name      string
  Age       int
  Joined    string
}

func main() {
    var repo gerpo.Repository[test] // Already initialized
    list, err := repo.GetList(ctxCache, func(m *test, h query.GetListHelper[test]) {
        h.Where().Field(&m.ID).LT(7) // Items with ID < 7
    })
    // Handle err and use the list
}

Order

Available for GetFirst/GetList:

package main

import (
    "context"
    "github.com/insei/gerpo"
    "github.com/insei/gerpo/query"
)

type test struct {
  ID        int
  CreatedAt time.Time
  UpdatedAt *time.Time
  Name      string
  Age       int
  Joined    string
}

func main() {
  var repo gerpo.Repository[test] // Already initialized
    item, err := repo.GetFirst(ctxCache, func(m *test, h query.GetFirstHelper[test]) {
        h.OrderBy().Field(&m.CreatedAt).DESC()
    })
    // Handle err and use 'item'
}

We hope this information helps you quickly get started with GERPO and integrate it into your own projects. If you have any questions or suggestions, feel free to open an issue or contribute to the repository.

About

GERPO is a generic repository pattern implementation with language integrated query support. Works with database/sql sources.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages