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

ScanMapRows

Use ScanMapRows to process database rows as dynamic map values.

Use ScanMapRows when you want each database row as a map[string]any.

This is useful when the result shape is dynamic, temporary, or not worth defining as a dedicated struct.

Typical use cases include:

  • admin screens
  • reports
  • exports
  • debugging queries
  • dynamic dashboards
  • generic tooling
  • queries with computed columns
  • queries where selected columns may change

Basic Idea

ScanMapRows scans each row into a map and passes it to a callback.

err := mapper.ScanMapRows(rows, func(row map[string]any) error {
	fmt.Println(row["id"], row["type"])
	return nil
})

The callback is called once for every row.

Each map key is the column name returned by the database driver.

Complete Query Example

This example loads event rows as dynamic maps.

package main

import (
	"database/sql"

	"github.com/netlifeguru/mapper"
)

func getEvents(db *sql.DB) ([]map[string]any, error) {
	rows, err := db.Query(`
		SELECT id, type, payload, created_at
		FROM events
		ORDER BY created_at DESC
	`)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var events []map[string]any

	err = mapper.ScanMapRows(rows, func(row map[string]any) error {
		events = append(events, row)
		return nil
	})

	if err != nil {
		return nil, err
	}

	return events, nil
}

Complete Usage Example

This example calls getEvents and prints the returned map 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()

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

	for _, event := range events {
		fmt.Printf(
			"ID: %v | Type: %v | Payload: %v | Created: %v\n",
			event["id"],
			event["type"],
			event["payload"],
			event["created_at"],
		)
	}
}

Returned Row Shape

Each row is represented as a map[string]any.

For the query above, one row may look like this:

map[string]any{
	"id":         int64(1),
	"type":       "user.created",
	"payload":    `{"name":"Alice"}`,
	"created_at": time.Now(),
}

The exact Go types depend on the database driver.

SQL Aliases

Map keys are based on returned column names.

Use SQL aliases when selecting computed values or joining tables.

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

The scanned map will contain keys such as:

map[string]any{
	"user_id":   int64(1),
	"user_name": "Alice",
	"role_name": "Admin",
}

Typed Access

For typed access, convert the map to mapper.Row.

row := mapper.Row(event)

eventType, ok := row.String("type")
if !ok {
	return errors.New("invalid event type")
}

mapper.Row provides helpers such as:

  • Int
  • Int64
  • String
  • Bool
  • Time

For individual values, you can also use standalone converters:

eventType, ok := mapper.AsString(event["type"])

When to Use ScanMapRows

Use ScanMapRows when:

  • you do not have a stable struct shape
  • you need dynamic columns
  • you are building reports or exports
  • you want to inspect raw row values
  • you want to process data as maps
  • you do not want to define a struct for the query

When Not to Use It

Do not use ScanMapRows when the result shape is known and stable.

Use struct scanning instead.

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

Structs make application code clearer and safer when the result has a known shape.

Notes

ScanMapRows consumes the rows.

Do not scan the same rows value again after calling it.

If the underlying rows object returns an error, ScanMapRows returns that error.

If the callback returns an error, scanning stops and returns that error.

Related Example

A standalone example is available in the examples repository:

ScanMapRows example

ScanStructOne

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

FillFromMap

Use FillFromMap to fill a Go struct from an existing map.

On this page

Basic IdeaComplete Query ExampleComplete Usage ExampleReturned Row ShapeSQL AliasesTyped AccessWhen to Use ScanMapRowsWhen Not to Use ItNotesRelated Example