NetLife Guru

Open source Go packages for fast, maintainable web systems. Built with a documentation-first approach.

Product
OverviewGolang packagesNews
Documentation
DocumentationGo LoggerGo RouterGo DB Form
Company
OverviewContactNewsGitHub
Community / Support
Supportinfo@netlife.guru
© 2026 NetLife Guru. All rights reserved.
GitHubinfo@netlife.guru
NetLife GuruNetLife GuruNetLife Guru
NetLife GuruNetLife GuruNetLife Guru
OverviewDocumentationNewsSupportContact

Golang packages

About
MappingDynamic RowsEdge Cases
Mapper

Mapping

Learn how mapper matches database columns to Go struct fields.

Mapper maps database columns to Go struct fields by name.

Instead of relying on scan position, mapper reads the column names returned by the database driver and matches them against exported struct fields.

This makes queries easier to maintain, especially when using aliases, joins, reordered columns, or SELECT *.

Basic Mapping

Use db tags to define how database columns should map to struct fields.

type User struct {
	ID        string    `db:"id"`
	Name      string    `db:"name"`
	Email     string    `db:"email"`
	Active    bool      `db:"active"`
	CreatedAt time.Time `db:"created_at"`
}

Example query:

rows, err := db.Query(`
	SELECT *
	FROM users
`)

The mapper matches each returned column to the corresponding struct field:

ColumnStruct field
idID
nameName
emailEmail
activeActive
created_atCreatedAt

Matching Order

When mapper builds field metadata for a struct, it resolves field names in this order:

  1. db tag
  2. json tag
  3. Go field name
  4. snake_case fallback based on the Go field name

This means the following struct can be mapped in multiple ways.

type User struct {
	ID        string `db:"id"`
	FullName  string `json:"full_name"`
	Email     string
	CreatedAt time.Time
}

Supported column names:

Struct fieldMatched column names
IDid
FullNamefull_name
EmailEmail, email
CreatedAtCreatedAt, created_at

The explicit db tag has the highest priority and should be preferred for database models.

Using db Tags

The db tag is the recommended way to describe database column names.

type Product struct {
	ID        int64     `db:"id"`
	Name      string    `db:"name"`
	Price     float64   `db:"price"`
	CreatedAt time.Time `db:"created_at"`
}

This keeps the mapping stable even if Go field names change later.

Using json Tags

If a field does not define a db tag, mapper can use the json tag.

type Product struct {
	ID        int64   `json:"id"`
	Name      string  `json:"name"`
	Price     float64 `json:"price"`
}

This is useful when the same struct is used for database mapping and JSON responses.

For database-specific structs, prefer db tags.

Field Name Fallback

If no db or json tag is present, mapper falls back to the Go field name.

type User struct {
	ID    string
	Name  string
	Email string
}

This can match columns such as:

SELECT ID, Name, Email FROM users

This fallback is useful for simple cases, but explicit tags are usually clearer.

Snake Case Fallback

Mapper also registers a snake_case version of each exported field name.

type User struct {
	CreatedAt time.Time
	UpdatedAt time.Time
}

This can match columns such as:

SELECT created_at, updated_at FROM users

So this struct works even without tags:

type User struct {
	CreatedAt time.Time
	UpdatedAt time.Time
}

For public documentation and long-term application code, explicit db tags are still recommended.

Ignoring Fields

Use db:"-" to exclude a field from mapping.

type User struct {
	ID       string `db:"id"`
	Name     string `db:"name"`
	Password string `db:"-"`
}

Ignored fields are not mapped from database columns.

The same behavior applies to json:"-" when no db tag is present.

Column Aliases

Column aliases are often useful when joining tables or returning computed values.

type UserWithRole struct {
	UserID   string `db:"user_id"`
	UserName string `db:"user_name"`
	RoleName string `db:"role_name"`
}
rows, err := db.Query(`
	SELECT
		u.id AS user_id,
		u.name AS user_name,
		r.name AS role_name
	FROM users u
	JOIN roles r ON r.id = u.role_id
`)

Mapper uses the returned column names, so aliases work naturally.

Extra Columns

If a query returns a column that has no matching struct field, mapper ignores it.

type User struct {
	ID   string `db:"id"`
	Name string `db:"name"`
}
SELECT id, name, created_at FROM users

In this case, created_at is ignored because the struct does not define a matching field.

This makes mapper friendly to SELECT *, joins, and queries that return additional values.

Missing Columns

If a struct field has no matching column in the result set, the field keeps its zero value.

type User struct {
	ID     string `db:"id"`
	Name   string `db:"name"`
	Active bool   `db:"active"`
}
SELECT id, name FROM users

The Active field is not present in the result set, so it remains the zero value for bool, which is false.

Type Assignment

Mapper assigns scanned values into struct fields using AssignValue.

This handles common Go types such as:

  • strings
  • booleans
  • signed and unsigned integers
  • floating point numbers
  • time.Time
  • pointers
  • byte slices
  • slices and maps from JSON values
  • nullable-style structs

Example:

type User struct {
	ID        int64     `db:"id"`
	Name      string    `db:"name"`
	Active    bool      `db:"active"`
	CreatedAt time.Time `db:"created_at"`
}

If a value cannot be assigned to the target field, mapper returns an error.

Pointer Fields

Pointer fields are supported.

type User struct {
	ID    string  `db:"id"`
	Name  string  `db:"name"`
	Email *string `db:"email"`
}

When the database value is not NULL, mapper allocates and assigns the pointer value.

When the source value is NULL, the pointer remains nil.

Nullable Values

Mapper supports nullable-style structs with a Valid field and a supported value field.

Supported value field names include:

  • String
  • Time
  • Bool
  • Int64
  • Float64

Example:

type NullString struct {
	String string
	Valid  bool
}

type User struct {
	ID    string     `db:"id"`
	Email NullString `db:"email"`
}

When the source value is NULL, the nullable struct is reset to its zero value.

When the source value is present, mapper assigns the value and sets Valid to true.

JSON Fields

Mapper can assign JSON strings or byte slices into slice and map fields.

type User struct {
	ID       string            `db:"id"`
	Tags     []string          `db:"tags"`
	Metadata map[string]string `db:"metadata"`
}

This is useful when a database column contains JSON data.

The database driver must return the JSON value as a string or []byte.

Custom Mapping

For cases where automatic field mapping is not enough, implement ScanMapper.

type User struct {
	ID   string
	Name string
}

func (u *User) ScanMap(row map[string]any) error {
	u.ID, _ = mapper.AsString(row["id"])
	u.Name, _ = mapper.AsString(row["name"])

	return nil
}

ScanMapper is useful when you need custom parsing, derived fields, fallback values, or non-standard column naming.

Recommended Style

For most application code, prefer explicit db tags.

type User struct {
	ID        string    `db:"id"`
	Name      string    `db:"name"`
	Email     string    `db:"email"`
	CreatedAt time.Time `db:"created_at"`
}

This makes the mapping clear, stable, and independent from JSON naming or Go field naming conventions.

Errors

API reference for mapper errors and recommended error handling patterns.

Dynamic Rows

Work with dynamic row maps, typed row helpers, and manual struct mapping.

On this page

Basic MappingMatching OrderUsing db TagsUsing json TagsField Name FallbackSnake Case FallbackIgnoring FieldsColumn AliasesExtra ColumnsMissing ColumnsType AssignmentPointer FieldsNullable ValuesJSON FieldsCustom MappingRecommended Style