Korm ORM and Framework for Go

repository·master·Indexed 41 days ago

https://github.com/kamalshkeir/korm

A high-performance, composable ORM and framework for Go inspired by Django. It features auto-migration, a built-in admin dashboard with PWA support, and a websocket-based PubSub system for data synchronization between instances. Korm supports SQLite, MySQL, MariaDB, Postgres, and CockroachDB, providing advanced querying, lifecycle hooks, and observability tools including PPROF and Prometheus metrics.

Tokens
25K
Snippets
101
Records
119
Agent score
51%

What's inside Korm

  1. Overview of Korm features

    master

    Korm is a high-performance ORM and framework for Go, inspired by the Django Framework. It is designed for concurrency and high throughput, offering features such as:

    • AutoMigrate: Automatically create or update database schemas directly from Go structs.
    • Admin Dashboard: A built-in dashboard with real-time log access via websockets and PWA support.
    • Shared Network Bus: A websocket-based PubSub system (WithBus) to synchronize data between multiple Korm instances.
    • Interactive Shell: A command-line interface for performing CRUD operations using korm.WithShell().
    • Built-in Authentication: Middleware support via korm.Auth, korm.Admin, or korm.BasicAuth.
    • Advanced Querying: Support for named queries (QueryNamed, WhereNamed), nested/embedded structs, and slice of structs through joins.
    • Hooks: Lifecycle hooks including OnInsert, OnSet, OnDelete, and OnDrop.
    • Observability: Integrated PPROF profiling, Prometheus metrics, and query logging via korm.LogsQueries().
    • Database Compatibility: Compatible with the standard database/sql package; use korm.GetConnection() to access the underlying connection.

    Supported Databases:

    • Sqlite
    • Mysql
    • Maria
    • Postgres
    • Cockroach
  2. Manage Many-to-Many Relationships

    master

    Korm supports Many-to-Many (M2M) relationships. To set them up, use korm.ManyToMany(table1, table2), which creates a join table named m2m_table1_table2.

    Operations:

    • Get Related (to struct): korm.Model[T]().GetRelated("relation_name", &target)
    • Get Related (to map): korm.Table("table").GetRelated("relation_name", &target)
    • Join Related (to struct): korm.Model[T]().JoinRelated("relation_name", &target)
    • Add Relation: korm.Model[T]().AddRelated("relation_name", "condition", args...)
    • Delete Relation: korm.Model[T]().DeleteRelated("relation_name", "condition", args...)
    // Setup
    korm.ManyToMany("classes", "students")
    
    // Fetching related students for a class named 'Math'
    var std []Student
    err := korm.Model[Class]().
        Where("name = ?", "Math").
        Select("name").
        OrderBy("-name").
        Limit(1).
        GetRelated("students", &std)
    
    // Adding a relation
    korm.Model[Class]().AddRelated("students", "name = ?", "hisName")
  3. Synchronize data between Korm instances with WithBus

    master

    Korm supports composability via a network websocket PubSub system using WithBus. This allows you to synchronize data between multiple Korm instances.

    Workflow:

    1. Initialize Korm.
    2. Call korm.WithBus(opts) to create a ksbus.Server.
    3. Use serverBus.SendToServer(address, data) to push data to another instance.
    4. Use korm.BeforeServersData(fn) to handle incoming data from other servers.
    5. Use korm.BeforeDataWS(fn) to handle authentication for websocket data.

    Example (Server 1):

    serverBus := korm.WithBus(ksbus.ServerOpts{...})
    serverBus.App.Get("/", func(c *ksmux.Context) {
        serverBus.SendToServer("localhost:9314", map[string]any{"msg": "hello"})
    })
    serverBus.Run("localhost:9313")
    serverBus := korm.WithBus(ksbus.ServerOpts{
        // ... configuration
    })
    serverBus.Run("localhost:9313")
  4. Define relationships using struct tags

    master

    Korm uses struct tags to define primary keys and foreign key relationships. You can define nested structs to represent one-to-many or many-to-one relationships.

    • Use `korm:"pk"` to mark a field as a primary key.
    • Use `korm:"fk:table.column:options"` to define a foreign key. Options like cascade can be appended to the tag (e.g., `korm:"fk:classes.id:cascade:cascade"`).
    type Class struct {
    	Id       uint `korm:"pk"` 
    	Name     string
    	Students []Student
    }
    
    type Student struct {
    	Id      uint `korm:"pk"` 
    	Name    string
    	Class   uint `korm:"fk:classes.id:cascade:cascade"` 
    	Classes Class
    }
  5. Run Korm benchmarks

    master

    To compare Korm's performance against GORM on your local machine, follow these steps:

    1. Clone the repository:
      git clone https://github.com/kamalshkeir/korm.git
      cd korm
    2. Prepare the benchmark file: Open benchmarks/bench-test.go and uncomment the relevant code sections.
    3. Initialize dependencies:
      go mod tidy
    4. Execute the benchmarks:
      go test -bench ^ .\benchmarks\ -benchmem
    go test -bench ^ .\benchmarks\ -benchmem
  6. Set up the Korm Admin Dashboard

    master

    Korm can provide a built-in Admin Dashboard and a CLI shell for managing data.

    Important Ordering Rule: If you are using the Dashboard, you must call korm.WithShell() after korm.WithDashboard().

    Basic Setup:

    serverBus := korm.WithDashboard(":9313")
    korm.WithShell() // Must be after WithDashboard
    
    // To create a superuser, run your app with the 'shell' command:
    // go run main.go shell
    
    serverBus.Run()

    Customizing the Dashboard: You can overwrite default settings like korm.PaginationPer, korm.DocsUrl, or the admin path via korm.SetAdminPath("/another").

    You can also customize the Auth and Admin middleware used by the dashboard by assigning new functions to korm.Auth and korm.Admin.

    serverBus := korm.WithDashboard(":9313")
    korm.WithShell()
    serverBus.Run()
  7. Install Korm database drivers

    master

    Korm drivers are located outside the main package to keep go.mod clean. Install the driver for your specific database using the following commands:

    SQLite: go get -u github.com/kamalshkeir/sqlitedriver@latest

    PostgreSQL / CockroachDB: go get -u github.com/kamalshkeir/pgdriver@latest

    MySQL / MariaDB: go get -u github.com/kamalshkeir/mysqldriver@latest

    go get -u github.com/kamalshkeir/sqlitedriver@latest
    go get -u github.com/kamalshkeir/pgdriver@latest
    go get -u github.com/kamalshkeir/mysqldriver@latest
  8. Configure the Router/Mux and Dashboard

    master

    Korm uses ksmux for routing. You can initialize a server with an Admin Dashboard using korm.WithDashboard(address). If you use the dashboard, you can also enable an interactive shell using korm.WithShell(), but ensure WithShell() is called after WithDashboard(). The dashboard provides a serverBus.App which is an http.Handler (the mux) where you can register global middlewares.

    func main() {
    	err := korm.New(korm.SQLITE, "db", sqlitedriver.Use())
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	serverBus := korm.WithDashboard("localhost:9313")
    	korm.WithShell() // Call after WithDashboard
    	mux := serverBus.App
    
    	// add global middlewares
    	mux.Use((midws ...func(http.Handler) http.Handler))
    }
  9. Use AutoMigrate to manage database schemas

    master

    Korm uses korm.AutoMigrate[T] to automatically create or update tables based on your Go structs. Use struct tags to define constraints.

    Common Struct Tags:

    • korm:"pk": Primary key (auto-incrementing).
    • korm:"size:N": Sets column size (e.g., VARCHAR(N)).
    • korm:"iunique": Case-insensitive unique constraint.
    • korm:"default:val": Sets a default value.
    • korm:"now": Automatically sets the current timestamp on creation.
    • korm:"update": Automatically updates the timestamp on model update.
    • korm:"fk:table.column:option": Foreign key with options like cascade, setnull, donothing, or setdefault.
    • korm:"text": Uses TEXT type instead of VARCHAR.
    • korm:"-": Ignores the field.

    Example:

    type User struct {
        Id        int       `korm:"pk"` 
        Email     string    `korm:"size:50;iunique"` 
        CreatedAt time.Time `korm:"now"` 
    }
    
    err := korm.AutoMigrate[User]("users")
    err := korm.AutoMigrate[User]("users")
  10. Configure the Admin Dashboard and Shell

    master

    Korm provides an Admin Dashboard and a CLI Shell for database management.

    Important Ordering Rule: If you use both WithDashboard and WithShell, you must call WithShell() after WithDashboard().

    1. korm.WithDashboard(address): Starts the admin dashboard at the specified address.
    2. korm.WithShell(): Enables the CLI shell.

    To create a superuser for the dashboard, run your application with the shell command: go run main.go shell.

    server := korm.WithDashboard("localhost:9313")
    korm.WithShell() // Must be after WithDashboard
    
    // ... later
    server.Run()
  11. Load Configuration from Environment with Kenv

    master

    Korm works well with kenv to map environment variables directly to Go structs using struct tags. Use the kenv:"ENV_VAR|default_value" tag format.

    • kenv.Load(".env") loads an environment file.
    • kenv.Fill(&configStruct) populates the struct with the loaded values.
    type GlobalConfig struct {
    	Host string `kenv:"HOST|localhost"` 
    	Db   struct {
    		Name string `kenv:"DB_NAME|db"` 
    		Type string `kenv:"DB_TYPE"` // Required
    	}
    }
    
    kenv.Load(".env")
    config := &GlobalConfig{}
    err := kenv.Fill(config)