Registration
Registration is the foundation of lit's performance. By registering models at startup, all reflection and query generation happens once, not on every database operation.
Why Registration Exists
Traditional ORMs use reflection on every query to map struct fields to database columns. This adds overhead to every operation. lit takes a different approach:
- Register once at application startup
- Use cached data for all subsequent operations
- Minimal reflection overhead during queries
Basic Registration
import "github.com/tracewayapp/lit/v2"
type User struct {
Id int
FirstName string
LastName string
Email string
}
func init() {
lit.RegisterModel[User](lit.PostgreSQL)
}Driver Registration
You can register default driver globally. This driver will be used for all models registered without specifying a driver.
lit.RegisterDriver(lit.PostgreSQL) // or lit.MySQL, lit.SQLite, lit.DuckDB
lit.RegisterModel[User]() // uses PostgreSQL driverIf for any reason you need to register a model with a different driver than the default, you can still specify it explicitly:
lit.RegisterDriver(lit.PostgreSQL)
lit.RegisterModel[User](lit.MySQL) // uses MySQL driverYou can also pass a custom driver to support databases beyond the built-in four.
What Gets Cached
When you call RegisterModel, lit creates a FieldMap containing:
| Field | Description |
|---|---|
ColumnsMap | Maps column names to field positions |
ColumnKeys | Ordered list of column names |
HasIntId | Whether id field is an integer (for auto-increment) |
InsertQuery | Pre-built INSERT statement |
UpdateQuery | Pre-built UPDATE statement (without WHERE clause) |
InsertColumns | Columns used in INSERT (excludes auto-increment id) |
Driver | Database driver (PostgreSQL, MySQL, SQLite, DuckDB, or custom) |
Default Naming Convention
lit converts Go's CamelCase to SQL's snake_case:
| Go Field | SQL Column |
|---|---|
Id | id |
FirstName | first_name |
LastName | last_name |
CreatedAt | created_at |
UserID | user_i_d |
Table names are derived from struct names with an s suffix:
User→usersProduct→products
ID Detection
lit automatically detects if your id field is an integer:
// Integer ID - uses DEFAULT in INSERT, RETURNING id (PostgreSQL) or LastInsertId (MySQL)
type User struct {
Id int // HasIntId = true
Name string
}
// String/UUID ID - includes id in INSERT values
type Session struct {
Id string // HasIntId = false
Data string
}Custom Naming Strategy
For different naming conventions, use RegisterModelWithNaming:
type MyNamingStrategy struct{}
func (MyNamingStrategy) GetTableNameFromStructName(name string) string {
return "tbl_" + strings.ToLower(name)
}
func (MyNamingStrategy) GetColumnNameFromStructName(name string) string {
return strings.ToLower(name)
}
func init() {
lit.RegisterModelWithNaming[User](lit.PostgreSQL, MyNamingStrategy{})
}See Naming Strategies for more details.
Generated Queries Example
For a registered User struct with PostgreSQL:
INSERT Query:
INSERT INTO users (id,first_name,last_name,email) VALUES (DEFAULT,$1,$2,$3) RETURNING idUPDATE Query (base):
UPDATE users SET id = $1,first_name = $2,last_name = $3,email = $4 WHEREThe WHERE clause is appended at runtime when you call Update().