Goe
A type safe SQL like ORM for Go
Install / Use
/learn @go-goe/GoeREADME
GOE
GO Entity or just "GOE" is a type safe ORM for Go
GOE logo by Luanexs
Requirements
- go v1.24 or above;
Real world examples
GOE has examples of queries and integrations, if you think in a nice example and wanted to see if GOE can handler, just try out.
Common examples are:
- How to use GOE with other frameworks;
- Where GOE key features can shine;
Features
Check out the Benchmarks section for a overview on GOE performance compared to other packages like, ent, GORM, sqlc, and others.
- 🚫 Non-String Usage;
- write queries with only Go code
- 🔖 Type Safety;
- get errors on compile time
- 📦 Auto Migrations;
- automatic generate tables from your structs
- 📄 SQL Like queries;
- use Go code to write queries with well known functions
- 🗂️ Iterator
- range over function to iterate over the rows
- 📚 Pagination
- paginate your large selects with just a function call
- ♻️ Wrappers
- wrappers for simple queries and builders for complex ones
Content
- Install
- Available Drivers
- Quick Start
- Database
- Select
- Insert
- Update
- Delete
- Transaction
- Benchmarks
Install
go get github.com/go-goe/goe
As any database/sql support in go, you have to get a specific driver for your database, check Available Drivers
Available Drivers
PostgreSQL
go get github.com/go-goe/postgres
Usage
type Animal struct {
// animal fields
}
type Database struct {
Animal *Animal
*goe.DB
}
dns := "user=postgres password=postgres host=localhost port=5432 database=postgres"
db, err := goe.Open[Database](postgres.Open(dns, postgres.NewConfig(postgres.Config{})))
SQLite
go get github.com/go-goe/sqlite
Usage
type Animal struct {
// animal fields
}
type Database struct {
Animal *Animal
*goe.DB
}
db, err := goe.Open[Database](sqlite.Open("goe.db", sqlite.NewConfig(sqlite.Config{})))
Checkout the exclusive features of sqlite on goe-sqlite
Quick Start
package main
import (
"fmt"
"github.com/go-goe/goe"
"github.com/go-goe/sqlite"
)
type Animal struct {
ID int
Name string
Emoji string
}
type Database struct {
Animal *Animal
*goe.DB
}
func main() {
db, err := goe.Open[Database](sqlite.Open("goe.db", sqlite.NewConfig(sqlite.Config{})))
if err != nil {
panic(err)
}
defer goe.Close(db)
err = goe.Migrate(db).AutoMigrate()
if err != nil {
panic(err)
}
err = goe.Delete(db.Animal).All()
if err != nil {
panic(err)
}
animals := []Animal{
{Name: "Cat", Emoji: "🐈"},
{Name: "Dog", Emoji: "🐕"},
{Name: "Rat", Emoji: "🐀"},
{Name: "Pig", Emoji: "🐖"},
{Name: "Whale", Emoji: "🐋"},
{Name: "Fish", Emoji: "🐟"},
{Name: "Bird", Emoji: "🐦"},
}
err = goe.Insert(db.Animal).All(animals)
if err != nil {
panic(err)
}
animals, err = goe.List(db.Animal).AsSlice()
if err != nil {
panic(err)
}
fmt.Println(animals)
}
To run the quick start follow this steps:
-
Init the go.mod file
go mod init quickstart -
Get the necessary packages:
go mod tidy -
Run the example:
go run main.go -
If everything was ok, you should see a output like this:
[{1 Cat 🐈} {2 Dog 🐕} {3 Rat 🐀} {4 Pig 🐖} {5 Whale 🐋} {6 Fish 🐟} {7 Bird 🐦}]
Database
type Database struct {
User *User
Role *Role
UserLog *UserLog
*goe.DB
}
In goe, it's necessary to define a Database struct, this struct implements *goe.DB and a pointer to all the structs that's it's to be mappend.
It's through the Database struct that you will interact with your database.
Supported Types
GOE supports any type that implements the Scanner Interface. Most common are sql.Null types from database/sql package.
type Table struct {
Price decimal.Decimal `goe:"type:decimal(10,4)"`
NullID sql.Null[uuid.UUID] `goe:"type:uuid"`
NullString sql.NullString `goe:"type:varchar(100)"`
}
Struct mapping
type User struct {
ID uint //this is primary key
Login string
Password string
}
[!NOTE] By default the field "ID" is primary key and all ids of integers are auto increment.
Setting primary key
type User struct {
Identifier uint `goe:"pk"`
Login string
Password string
}
In case you want to specify a primary key use the tag value "pk".
Setting type
type User struct {
ID string `goe:"pk;type:uuid"`
Login string `goe:"type:varchar(10)"`
Name string `goe:"type:varchar(150)"`
Password string `goe:"type:varchar(60)"`
}
You can specify a type using the tag value "type"
Setting null
type User struct {
ID int
Name string
Email *string // this will be a null column
Phone sql.NullString `goe:"type:varchar(20)"` // also null
}
[!IMPORTANT] A pointer is considered a null column in Database.
Setting default
type User struct {
ID int
Name string
Email *string
CreatedAt time.Time `goe:"default:current_timestamp"`
}
A default value is used when the field is inserted with no value.
// CreatedAt will have the default value
err = goe.Insert(db.User).One(&User{Name: "Rose"})
if err != nil {
// handler error
}
Relationship
In GOE relational fields are created using the pattern TargetTable+TargetTableID, so if you want to have a foreign key to User, you will have to write a field like UserID or UserIDOrigin.
One To One
type User struct {
ID uint
Login string
Name string
Password string
}
type UserDetails struct {
ID uint
Email string
Birthdate time.Time
UserID uint // one to one with User
}
Many To One
For simplifications all relational slices should be the last fields on struct.
type User struct {
ID uint
Name string
Password string
UserLogs []UserLog // one User has many UserLogs
}
type UserLog struct {
ID uint
Action string
DateTime time.Time
UserID uint // if remove the slice from user, will become a one to one
}
The difference from one to one and many to one it's a slice field on the "many" struct.
Many to Many
For simplifications all relational slices should be the last fields on struct.
Using implicit many to many:
type Person struct {
ID int
Name string
Jobs []Job // Person has a slice to Jobs
}
// Person and Job are implicit relational
type PersonJob struct {
PersonID int `goe:"pk"`
JobID int `goe:"pk"`
CreatedAt time.Time
}
type Job struct {
Name string
ID int
Persons []Person // Job has a slice to Person
}
[!IMPORTANT] It's used the tags "pk" for ensure that the foreign keys will be both primary key.
Using many to one pattern:
type User struct {
ID uint
Name string
Password string
UserRoles []UserRole
}
type UserRole struct {
UserID uint `goe:"pk"`
RoleID uint `goe:"pk"`
}
type Role struct {
ID uint
Name string
UserRoles []UserRole
}
Is used a combination of two many to one to generate a many to many. In this example, User has many UserRole and Role has many UserRole.
[!IMPORTANT] It's used the tags "pk" for ensure that the foreign keys will be both primary key.
[Back to Contents](#conte
