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
ScanStructRowsScanStructSliceScanStructOneScanMapRowsFillFromMap
MappingDynamic RowsEdge Cases
MapperReal Usage

ScanStructSlice

Use ScanStructSlice to load database rows into a typed Go slice.

Use ScanStructSlice when you want to scan all database rows into a typed Go slice.

This is the simplest mapper function for list queries.

It is useful when you expect zero or more rows and want the result as []T.

When to Use It

Use ScanStructSlice when:

  • you are building a list query
  • you want all rows returned as a slice
  • the result set can safely fit in memory
  • you do not need custom per-row callback logic
  • the caller expects []T

For callback-based row processing, use ScanStructRows.

Define a Struct

Create a struct that represents one row from the query result.

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 db tags tell mapper which database columns should be assigned to each field.

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()

Scan Into a Slice

Pass the rows into ScanStructSlice.

return mapper.ScanStructSlice[User](rows)

Mapper scans every row into a User value and returns a []User.

Complete Query Example

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

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"),
		)
	}
}

The connectDB function is part of the standalone example and is responsible for opening the MySQL database connection.

Empty Result

When the query returns no rows, ScanStructSlice returns an empty slice.

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

fmt.Println(len(users)) // 0

This is useful for API list responses where an empty result is valid.

Difference From ScanStructRows

ScanStructSlice automatically builds the slice for you.

users, err := mapper.ScanStructSlice[User](rows)

With ScanStructRows, you control what happens for every scanned row.

var users []User

err = mapper.ScanStructRows[User](rows, func(user *User) error {
	users = append(users, *user)
	return nil
})

Both approaches are valid.

Use ScanStructSlice for simple list queries.

Use ScanStructRows when you need callback control.

Rows Are Consumed

Rows are consumed during scanning.

Do not scan the same rows value twice.

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

// Do not scan the same rows again.
// Run the query again if you need another scan.

Related Example

A standalone example is available in the examples repository:

ScanStructSlice example

ScanStructRows

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

ScanStructOne

Use ScanStructOne when a query is expected to return exactly one database row.

On this page

When to Use ItDefine a StructQuery RowsScan Into a SliceComplete Query ExampleComplete Usage ExampleEmpty ResultDifference From ScanStructRowsRows Are ConsumedRelated Example