by @encoredev
Database queries and migrations with Encore Go.
package user
import "encore.dev/storage/sqldb"
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
Encore provides type-safe query methods using generics.
Query - Multiple Rowstype User struct {
ID string
Email string
Name string
}
func listActiveUsers(ctx context.Context) ([]*User, error) {
rows, err := sqldb.Query[User](ctx, db, `
SELECT id, email, name FROM users WHERE active = true
`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
users = append(users, rows.Value())
}
return users, rows.Err()
}
QueryRow - Single Rowfunc getUser(ctx context.Context, id string) (*User, error) {
user, err := sqldb.QueryRow[User](ctx, db, `
SELECT id, email, name FROM users WHERE id = $1
`, id)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return user, nil
}
Exec - No Return ValueFor INSERT, UPDATE, DELETE operations:
func createUser(ctx context.Context, email, name string) error {
_, err := sqldb.Exec(ctx, db, `
INSERT INTO users (id, email, name)
VALUES ($1, $2, $3)
`, generateID(), email, name)
return err
}
func updateUser(ctx context.Context, id, name string) error {
_, err := sqldb.Exec(ctx, db, `
UPDATE users SET name = $1 WHERE id = $2
`, name, id)
return err
}
func deleteUser(ctx context.Context, id string) error {
_, err := sqldb.Exec(ctx, db, `
DELETE FROM users WHERE id = $1
`, id)
return err
}
user/
└── migrations/
├── 1_create_users.up.sql
├── 2_add_...