List
Use db.List to read multiple database rows into a typed Go slice.
Use db.List when a query is expected to return multiple rows.
It scans all returned rows into a typed Go slice.
users, err := db.List[User](ctx, conn, selectUsersQuery, 10)
if err != nil {
return nil, err
}db.List is the most common helper for list endpoints and collection-style reads.
Function
func List[T any](ctx context.Context, c db.Conn, query string, args ...any) ([]T, error)List accepts:
- a
context.Context - a
db.Conn - a SQL or CQL query string
- optional query arguments
It returns:
[]T, errorWhen to Use List
Use db.List when:
- the query can return zero or more rows
- the result shape is known
- you want a typed
[]T - the result set can safely fit in memory
- you are building list endpoints, reports, or search results
For very large result sets, prefer streaming patterns where appropriate.
Define a Model
Create a struct that represents one returned row.
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 struct is mapped using the mapper package.
The db tags match returned column names.
Query
The query can be written directly in Go code.
For MySQL and Scylla, use ? placeholders.
const selectUsersQuery = `
SELECT *
FROM users
ORDER BY created_at DESC
LIMIT ?
`For PostgreSQL, use numbered placeholders.
const selectUsersQuery = `
SELECT *
FROM users
ORDER BY created_at DESC
LIMIT $1
`Complete Query Example
This example returns users as a typed slice.
package main
import (
"context"
"database/sql"
"time"
"github.com/netlifeguru/db"
)
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"`
}
const selectUsersQuery = `
SELECT *
FROM users
ORDER BY created_at DESC
LIMIT ?
`
func ListUsers(ctx context.Context, conn db.Conn, limit int) ([]User, error) {
return db.List[User](ctx, conn, selectUsersQuery, limit)
}Complete Usage Example
This example connects to the database, loads users, and prints the result.
package main
import (
"context"
"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")
}
conn, err := connectDB()
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
users, err := ListUsers(ctx, conn, 10)
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"),
)
}
}Empty Results
If the query returns no rows, db.List returns an empty slice.
users, err := db.List[User](ctx, conn, selectUsersQuery, 10)
if err != nil {
return nil, err
}
fmt.Println(len(users)) // 0An empty result is not treated as an error.
This makes db.List a good fit for list endpoints and search results.
Driver Placeholder Differences
The Go call stays the same across drivers.
users, err := db.List[User](ctx, conn, selectUsersQuery, 10)Only the query placeholder syntax changes.
| Driver | Placeholder |
|---|---|
| MySQL | ? |
| Postgres | $1 |
| Scylla | ? |
Scylla Example
For Scylla, queries are usually based on query tables.
const selectUsersByStatusQuery = `
SELECT *
FROM users_by_status
WHERE status = ?
LIMIT ?
`
func ListUsersByStatus(ctx context.Context, conn db.Conn, status string, limit int) ([]User, error) {
return db.List[User](ctx, conn, selectUsersByStatusQuery, status, limit)
}Related Helpers
Use db.Get when the query should return zero or one row.
user, found, err := db.Get[User](ctx, conn, selectUserQuery, id)Use db.Value when the query returns a single scalar value.
total, found, err := db.Value[int64](ctx, conn, countUsersQuery)Use db.Maps when the result shape is dynamic.
rows, err := db.Maps(ctx, conn, selectRowsQuery)Related Examples
Standalone examples are available in the examples repository: