Bun Writer
Generates Go source files with Bun model definitions from database schema information.
Overview
The Bun Writer converts RelSpec's internal database model representation into Go source code with Bun struct definitions, complete with proper tags, relationships, and table configuration.
With --types sqltypes, nullable fields use the pkg/sqltypes package.
Features
- Generates Bun-compatible Go structs
- Creates proper
bunstruct tags - Adds relationship fields
- Supports both single-file and multi-file output
- Maps SQL types to Go types
- Handles nullable fields with sql.Null* types
- Generates table aliases
Usage
Basic Example
package main
import (
"git.warky.dev/wdevs/relspecgo/pkg/models"
"git.warky.dev/wdevs/relspecgo/pkg/writers"
"git.warky.dev/wdevs/relspecgo/pkg/writers/bun"
)
func main() {
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
}
writer := bun.NewWriter(options)
err := writer.WriteDatabase(db)
if err != nil {
panic(err)
}
}
CLI Examples
# Generate Bun models from a DBML schema (default: baselib pointer types)
relspec convert --from dbml --from-path schema.dbml \
--to bun --to-path models.go --package models
# Use standard library database/sql nullable types instead
relspec convert --from dbml --from-path schema.dbml \
--to bun --to-path models.go --package models \
--types stdlib
# Select sqltypes package types (git.warky.dev/wdevs/relspecgo/pkg/sqltypes)
relspec convert --from pgsql --from-conn "postgres://localhost/mydb" \
--to bun --to-path models.go --package models \
--types sqltypes
# Multi-file output (one file per table)
relspec convert --from json --from-path schema.json \
--to bun --to-path models/ --package models
# Inject computed/scan-only fields that are not present in the database schema
# (extra-fields.json contains the JSON array shown below)
relspec convert --from dbml --from-path schema.dbml \
--to bun --to-path models.go --package models \
--extra-fields extra-fields.json
Generated Code Examples
sqltypes package types (--types sqltypes)
package models
import (
sql_types "git.warky.dev/wdevs/relspecgo/pkg/sqltypes"
"github.com/uptrace/bun"
)
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:"id,type:uuid,pk," json:"id"`
Username string `bun:"username,type:text,notnull," json:"username"`
Email sql_types.SqlString `bun:"email,type:text,nullzero," json:"email"`
Tags sql_types.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
CreatedAt sql_types.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
}
Standard library — --types stdlib
package models
import (
"database/sql"
"time"
"github.com/uptrace/bun"
)
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID string `bun:"id,type:uuid,pk," json:"id"`
Username string `bun:"username,type:text,notnull," json:"username"`
Email sql.NullString `bun:"email,type:text,nullzero," json:"email"`
Tags []string `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
CreatedAt time.Time `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
}
Supported Bun Tags
table- Table name and aliascolumn- Column name (auto-derived if not specified)pk- Primary keyautoincrement- Auto-incrementnotnull- NOT NULL constraintunique- Unique constraintdefault- Default valuerel- Relationship definitiontype- Explicit SQL type
Type Mapping
The nullable type package is selected with --types (or WriterOptions.NullableTypes).
| SQL Type | NOT NULL (both) | Nullable — sqltypes | Nullable — stdlib |
|---|---|---|---|
bigint |
int64 |
SqlInt64 |
sql.NullInt64 |
integer |
int32 |
SqlInt32 |
sql.NullInt32 |
smallint |
int16 |
SqlInt16 |
sql.NullInt16 |
text, varchar |
string |
SqlString |
sql.NullString |
boolean |
bool |
SqlBool |
sql.NullBool |
timestamp, timestamptz |
time.Time* |
SqlTimeStamp |
sql.NullTime |
numeric, decimal |
float64 |
SqlFloat64 |
sql.NullFloat64 |
uuid |
string |
SqlUUID |
sql.NullString |
jsonb |
string |
SqlJSONB |
sql.NullString |
text[] |
SqlStringArray |
SqlStringArray |
[]string |
integer[] |
SqlInt32Array |
SqlInt32Array |
[]int32 |
uuid[] |
SqlUUIDArray |
SqlUUIDArray |
[]string |
vector |
SqlVector |
SqlVector |
[]float32 |
* In sqltypes mode, NOT NULL timestamps use SqlTimeStamp (not time.Time) unless the base type is a simple integer or boolean. In stdlib mode, NOT NULL timestamps use time.Time.
Writer Options
NullableTypes
Controls which Go package is used for nullable column types. Set via the --types CLI flag or WriterOptions.NullableTypes:
// Use sqltypes package types
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
NullableTypes: writers.NullableTypeSqlTypes,
}
// Use standard library database/sql types
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
NullableTypes: writers.NullableTypeStdlib,
}
Metadata Options
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
Metadata: map[string]any{
"multi_file": true, // Enable multi-file mode
"populate_refs": true, // Populate RefDatabase/RefSchema
"generate_get_id_str": true, // Generate GetIDStr() methods
},
}
Extra fields
Use Metadata["extra_fields"] (or the CLI --extra-fields <path> flag) to inject
custom fields into generated Bun models without editing generated files. This is
intended for computed columns and scan-only query fields that Bun must scan into
but that are not real database table columns.
The CLI flag expects a path to a JSON file, not inline JSON. This avoids shell quoting problems with struct tags and complex type names.
Each entry supports:
| Field | Required | Description |
|---|---|---|
target_table |
No | Optional model scope. Accepts table name (projects), qualified table (public.projects), or model name (ModelPublicProjects). If omitted, the field is added to every generated model. |
name |
Yes | Go struct field name. |
type |
Yes | Go type to emit. |
bun_tag |
No | Raw Bun tag contents, e.g. thought_count,scanonly. |
json_tag |
No | Raw JSON tag contents. |
comment |
No | Optional line comment. |
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
Metadata: map[string]any{
"extra_fields": `[
{
"target_table": "projects",
"name": "ThoughtCount",
"type": "sql_types.SqlInt64",
"bun_tag": "thought_count,scanonly",
"json_tag": "thought_count",
"comment": "Computed by ResolveSpec queries"
}
]`,
},
}
Example extra-fields.json:
[
{
"target_table": "projects",
"name": "ThoughtCount",
"type": "sql_types.SqlInt64",
"bun_tag": "thought_count,scanonly",
"json_tag": "thought_count",
"comment": "Computed by ResolveSpec queries"
}
]
Notes
- Model names are derived from table names (singularized, PascalCase)
- Table aliases are auto-generated from table names
- Nullable columns use plain Go pointer types (
*string,*time.Time, …) by default; pass--types sqltypesto usesql_types.SqlString,sql_types.SqlTimeStamp, etc., or--types stdlibto usesql.NullString,sql.NullTime, etc. - Array columns use plain Go slices (
[]string,[]int32, …) by default;--types sqltypesusessql_types.SqlStringArray,sql_types.SqlInt32Array, etc. - Multi-file mode: one file per table named
sql_{schema}_{table}.go - Generated code is auto-formatted
- JSON tags are automatically added