apigen

command module
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 8 Imported by: 0

README

apigen

apigen is a tool written in Go that parses Go structs and generates code for querying the database based on GORM ORM. It also includes rawgen, a companion tool that generates raw database/sql PostgreSQL code for when you need low-level control and maximum performance.

Features

  • Generates GORM services for all structs in your models unless skipped in the configuration file (apigen.toml)
  • Created database connection helper with sane defaults
  • Optionally preloads all relationships (even nested relationships) by default. Because the parser knows foreign keys and the tree, we are able to do that for all foreignKey and many2many fields
  • Allows for customizing all queries by specifying optional Where, ordering, grouping, select options ...services.Options. These options are passed to the callable handlers that are designed with the decorator pattern
  • Generates typescript interfaces for your models
  • rawgen — generates raw PostgreSQL Go functions (database/sql) for Insert, Get, Delete, Update, and Query with full control over selected fields, omitted fields, custom filters, and table names

Performance Tuning

apigen includes configuration options to reduce query overhead in the generated GORM services:

# Set to true to disable automatic preloading — callers opt in per-query
# via .PreloadAll(true) or the Preload() option
LazyPreload = false

# Set to false to skip re-fetching records after Create/Update
# Callers can call .Get(id) explicitly when they need associations loaded
RefetchAfterWrite = true

You can also override those defaults per generated method. Supported method keys are:

  • Get
  • GetAll
  • GetPaginated
  • FindOne
  • FindMany
  • Create
  • CreateMany
  • Update
  • PartialUpdate
  • PartialUpdateWithMap

Each method override can set:

  • PreloadAll
  • RefetchAfterWrite

Example:

[Queries.Default.GetAll]
PreloadAll = false

[Queries.Default.FindMany]
PreloadAll = false

[Queries.Models.User.Get]
PreloadAll = true

[Queries.Models.User.Create]
RefetchAfterWrite = false

[Queries.Models.User.Update]
PreloadAll = true
RefetchAfterWrite = true

Override precedence is top-level config, then Queries.Default, then Queries.Models.<Model>.

At runtime, generated services still allow per-call overrides:

  • .PreloadAll(true) or .PreloadAll(false) overrides the configured preload default for that service instance.
  • .Preload("Relation") disables the configured automatic preloads for that call and applies only the explicit preload(s) you pass.
  • Write methods only re-fetch when the effective RefetchAfterWrite is true.

The generated services also include generic raw SQL helpers for escape-hatch queries:

// For complex queries, reports, aggregates, CTEs
results, err := services.RawQuery[MyStruct](db, "SELECT * FROM users WHERE age > $1", 18)
row, err := services.RawQueryRow[MyStruct](db, "SELECT * FROM users WHERE id = $1", 1)

Installation

apigen CLI
go install github.com/abiiranathan/apigen@latest
rawgen CLI (separate binary)
go install github.com/abiiranathan/apigen/cmd/rawgen@latest
As a library
go get -u github.com/abiiranathan/apigen
Initialize project and create a configuration file apigen.toml
apigen init

The configuration file has the following format:

# apigen configuration file.
# Strings should use single quotes

# PreloadAll all relationships with gorm.Preload
PreloadAll = false

# PreloadDepth is the depth of the relationships to preload
# e.g Patient.Visit.Doctor will be preloaded if PreloadDepth is 3
PreloadDepth = 3

# Output preload.json
OutputJson = true

# LazyPreload overrides top-level PreloadAll and makes the default no-preload.
# Query-specific overrides can still enable preloading for selected methods.
# LazyPreload = false

# RefetchAfterWrite defaults to true when omitted.
# Query-specific overrides can disable it for selected methods.
# RefetchAfterWrite = true

[Queries.Default.GetAll]
PreloadAll = false

[Queries.Default.FindMany]
PreloadAll = false

[Queries.Models.User.Get]
PreloadAll = true

[Queries.Models.User.Create]
RefetchAfterWrite = false

[Queries.Models.User.Update]
PreloadAll = true
RefetchAfterWrite = true

[Models]
# ModelPkg is the package name for the models to look for struct definitions
Pkgs = [
  'github.com/abiiranathan/apigen/models',
]

# Skip is a list of models to skip. Names are case sensitive
Skip = []

# ReadOnly is a list of models that should not be generated with CRUD operations
# Only Get methods will be generated for these models.
ReadOnly = []

[Output]

# OutDir is the directory to write the generated files to
OutDir = 'gen'

# ServiceName is the name of the service to generate
ServiceName = 'services'

[overrides]
# Overrides for types and fields. Forexample, here we are overriding the type
# as would appear in typescript for Sex to be an enum.
[overrides.types]
Sex = '"Male" | "Female"'

# Here we are overriding gener to of type Sex. Sex is a go type.
[overrides.fields]
gender = 'Sex'

Generate code

If apigen.toml is the root of your project.

apigen generate

Or

apigen --config config.toml generate

to specify the custom path to the configuration file.

If you change Queries settings, regenerate services so the new defaults are baked into the generated code.

Using the generated code

import(
    // These will point to the generated code
    "github.com/yourname/cool-api/gen/handlers"
	"github.com/yourname/cool-api/gen/services"

	"gorm.io/gorm/logger"
)

func main() {
	db, err := services.PostgresConnection(dsn, "Africa/kampala", logger.Silent, os.Stdout)
	if err != nil {
		panic(err)
	}

	err = db.AutoMigrate(&models.User{})
	if err != nil {
		log.Fatalf("Error performing database migrations: %v\n", err)
	}

	svc := services.NewService(db)
    users, err := svc.Users.GetAll()

  // Override the configured defaults for a specific call chain.
  user, err := svc.Users.PreloadAll(true).Get(1)
  filtered, err := svc.Users.Preload("Role").FindMany(services.Where("age > ?", 18))
}

rawgen — Raw PostgreSQL Code Generator

rawgen generates type-safe database/sql Go functions for a given model. It reads your apigen.toml to find model packages and outputs Go code to stdout. Use it when you need low-level control over SQL without GORM overhead.

Install
go install github.com/abiiranathan/apigen/cmd/rawgen@latest
Usage
rawgen [flags]

Flags:
  -config string          Path to apigen.toml (default "apigen.toml")
  -model string           Model struct name (required, e.g. "User")
  -table string           Override table name (default: snake_case plural of model)
  -select string          Comma-separated field names to include (default: all)
  -omit string            Comma-separated field names to exclude
  -filter string          Custom filters as "Column:Op:GoType" (comma-separated)
  -nullable-filter string Same as -filter but wraps in nil check (pointer param)
Examples

Basic — all fields, no filters:

go run ./cmd/rawgen -model User > queries/user.go

Generated output:

func InsertUser(ctx context.Context, db *sql.DB, u *models.User) error { ... }
func GetUser(ctx context.Context, db *sql.DB, id int) (*models.User, error) { ... }
func QueryUsers(ctx context.Context, db *sql.DB) ([]*models.User, error) { ... }
func UpdateUser(ctx context.Context, db *sql.DB, u *models.User) error { ... }
func DeleteUser(ctx context.Context, db *sql.DB, id int) error { ... }

Select specific fields:

go run ./cmd/rawgen -model User -select "ID,Name,Age"

Only id, name, age columns appear in all generated SQL — no discount, no role_id.

Omit fields:

go run ./cmd/rawgen -model User -omit "Discount"

All columns except discount are included.

With required filters (typed function params):

go run ./cmd/rawgen -model User -filter "Age:>:int,Name:ILIKE:string"

Generates QueryUsers with typed parameters that build a dynamic WHERE clause:

func QueryUsers(ctx context.Context, db *sql.DB, age int, name string) ([]*models.User, error) {
    // ... WHERE age > $1 AND name ILIKE $2
}

With optional/nullable filters (pointer params, nil-checked):

go run ./cmd/rawgen -model User -nullable-filter "Age:>:int"

Generates QueryUsers with pointer parameters — filters are only applied when non-nil:

func QueryUsers(ctx context.Context, db *sql.DB, age *int) ([]*models.User, error) {
    // if age != nil { ... WHERE age > $1 }
}

Custom table name:

go run ./cmd/rawgen -model User -table my_users

All generated SQL uses my_users instead of the default users.

Pipe to a file and format:

go run ./cmd/rawgen -model User -filter "Age:>:int" > queries/user.go && gofmt -w queries/user.go

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
eclinic command
rawgen command
Package config provides functionality to load and validate the configuration for the apigen tool.
Package config provides functionality to load and validate the configuration for the apigen tool.
Package models defines the data models used in the application, including User, Role, Tag, Issue, Question, and Comment.
Package models defines the data models used in the application, including User, Role, Tag, Issue, Question, and Comment.
Package parser provides functionality to parse Go source files, extract struct metadata, and generate GORM services based on that metadata.
Package parser provides functionality to parse Go source files, extract struct metadata, and generate GORM services based on that metadata.
Package rawgen generates raw PostgreSQL Go code for a given model struct.
Package rawgen generates raw PostgreSQL Go code for a given model struct.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL