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
InstallationQuick Start
MappingDynamic RowsEdge Cases
MapperGetting Started

Quick Start

Scan database rows into a typed Go slice.

This guide shows the basic mapper workflow:

  1. define a Go struct
  2. query rows from a database
  3. scan the rows into a typed Go slice

The examples use MySQL through Go’s standard database/sql package, but the same mapping concept applies to any rows compatible with the mapper.Rows interface.

Define a Struct

Create a struct that represents one database row.

Use db tags to match struct fields with database column names.

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

The mapper matches columns by name, not by scan position.

For example, the created_at column is mapped into the CreatedAt field using the db:"created_at" tag.

Query Rows

Query rows using your database driver.

rows, err := db.Query(`
	SELECT *
	FROM users
	ORDER BY created_at DESC
`)

if err != nil {
	return nil, err
}

defer rows.Close()

The rows value must be compatible with the mapper.Rows interface.

Go’s standard *sql.Rows already provides the required behavior.

Scan Into a Slice

Use ScanStructSlice to load all returned rows into a typed slice.

users, err := mapper.ScanStructSlice[User](rows)
if err != nil {
	return nil, err
}

The result is a regular Go slice:

for _, user := range users {
	fmt.Println(user.Name)
}

Complete Query Example

This example defines the model and a small repository-style function.

package main

import (
	"database/sql"
	"time"

	"github.com/netlifeguru/mapper"
)

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

func getUsers(db *sql.DB) ([]User, error) {
	rows, err := db.Query(`
		SELECT *
		FROM users
		ORDER BY created_at DESC
	`)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	return mapper.ScanStructSlice[User](rows)
}

Complete Usage Example

This example connects to the database, loads users, and prints the scanned values.

package main

import (
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
	"github.com/joho/godotenv"
)

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Println(".env file not found, I'm using system env variables")
	}

	db, err := connectDB()
	if err != nil {
		log.Fatal(err)
	}

	defer db.Close()

	users, err := getUsers(db)
	if err != nil {
		log.Fatal(err)
	}

	for _, user := range users {
		fmt.Printf(
			"ID: %d | Name: %s | Email: %s | Active: %t | Created: %s\n",
			user.ID,
			user.Name,
			user.Email,
			user.Active,
			user.CreatedAt.Format("2006-01-02 15:04:05"),
		)
	}
}

Next Steps

Use ScanStructSlice for common list queries.

Use ScanStructOne when the query should return exactly one row.

Use ScanStructRows when you want to process rows one by one through a callback.

Use ScanMapRows when the result shape is dynamic and you want map-based rows.

Related Example

A standalone example is available in the examples repository:

ScanStructSlice example

Installation

Install the mapper package into your Go project.

ScanStructRows

Use ScanStructRows to process database rows one by one with a callback.

On this page

Define a StructQuery RowsScan Into a SliceComplete Query ExampleComplete Usage ExampleNext StepsRelated Example