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:
| Column | Struct field |
|---|---|
id | ID |
name | Name |
email | Email |
active | Active |
created_at | CreatedAt |
Matching Order
When mapper builds field metadata for a struct, it resolves field names in this order:
dbtagjsontag- Go field name
- 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 field | Matched column names |
|---|---|
ID | id |
FullName | full_name |
Email | Email, email |
CreatedAt | CreatedAt, 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 usersThis 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 usersSo 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 usersIn 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 usersThe 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:
StringTimeBoolInt64Float64
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.