encore-go-database by encoredev/skills
npx skills add https://github.com/encoredev/skills --skill encore-go-databasepackage user
import "encore.dev/storage/sqldb"
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
Encore 的数据库 API 与 Go 的标准 database/sql 包保持一致。使用 .Scan() 将查询结果读取到变量中。
Query - 多行查询type User struct {
ID string
Email string
Name string
}
func listActiveUsers(ctx context.Context) ([]*User, error) {
rows, err := db.Query(ctx, `
SELECT id, email, name FROM users WHERE active = true
`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {
return nil, err
}
users = append(users, &u)
}
return users, rows.Err()
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
QueryRow - 单行查询func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
Exec - 无返回值操作用于 INSERT、UPDATE、DELETE 操作:
func createUser(ctx context.Context, email, name string) error {
_, err := db.Exec(ctx, `
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 := db.Exec(ctx, `
UPDATE users SET name = $1 WHERE id = $2
`, name, id)
return err
}
func deleteUser(ctx context.Context, id string) error {
_, err := db.Exec(ctx, `
DELETE FROM users WHERE id = $1
`, id)
return err
}
user/
└── migrations/
├── 1_create_users.up.sql
├── 2_add_posts.up.sql
└── 3_add_indexes.up.sql
.up.sql 结尾-- migrations/1_create_users.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
func transferFunds(ctx context.Context, fromID, toID string, amount int) error {
tx, err := db.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback() // 如果已提交则无操作
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance - $1 WHERE id = $2
`, amount, fromID)
if err != nil {
return err
}
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance + $1 WHERE id = $2
`, amount, toID)
if err != nil {
return err
}
return tx.Commit()
}
Scan 方法将查询结果中的列读取到变量中。列是按位置映射的,而不是按名称映射的——Scan 参数的顺序必须与 SELECT 语句中列的顺序匹配。
type User struct {
ID string
Email string
Name string
CreatedAt time.Time
}
// 使用 QueryRow 查询单行
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name, created_at FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)
if err != nil {
return nil, err
}
return &u, nil
}
// 也可以扫描到内联结构体
func getItem(ctx context.Context, id int64) error {
var item struct {
ID int64
Title string
Done bool
}
err := db.QueryRow(ctx, `
SELECT id, title, done FROM items WHERE id = $1
`, id).Scan(&item.ID, &item.Title, &item.Done)
return err
}
始终使用参数化查询:
// 安全 - 值已参数化
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE email = $1
`, email).Scan(&u.ID, &u.Email, &u.Name)
// 错误 - 存在 SQL 注入风险
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
import (
"errors"
"encore.dev/storage/sqldb"
"encore.dev/beta/errs"
)
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
$1、$2 等)Scan 读取查询结果 - 列按位置映射sqldb.ErrNoRows每周安装量
121
代码仓库
GitHub 星标数
20
首次出现
2026年1月21日
安全审计
已安装于
opencode102
gemini-cli101
codex101
claude-code94
github-copilot83
cursor79
package user
import "encore.dev/storage/sqldb"
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
Migrations: "./migrations",
})
Encore's database API mirrors Go's standard database/sql package. Use .Scan() to read query results into variables.
Query - Multiple Rowstype User struct {
ID string
Email string
Name string
}
func listActiveUsers(ctx context.Context) ([]*User, error) {
rows, err := db.Query(ctx, `
SELECT id, email, name FROM users WHERE active = true
`)
if err != nil {
return nil, err
}
defer rows.Close()
var users []*User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Email, &u.Name); err != nil {
return nil, err
}
users = append(users, &u)
}
return users, rows.Err()
}
QueryRow - Single Rowfunc getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
Exec - No Return ValueFor INSERT, UPDATE, DELETE operations:
func createUser(ctx context.Context, email, name string) error {
_, err := db.Exec(ctx, `
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 := db.Exec(ctx, `
UPDATE users SET name = $1 WHERE id = $2
`, name, id)
return err
}
func deleteUser(ctx context.Context, id string) error {
_, err := db.Exec(ctx, `
DELETE FROM users WHERE id = $1
`, id)
return err
}
user/
└── migrations/
├── 1_create_users.up.sql
├── 2_add_posts.up.sql
└── 3_add_indexes.up.sql
.up.sql-- migrations/1_create_users.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
func transferFunds(ctx context.Context, fromID, toID string, amount int) error {
tx, err := db.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback() // No-op if committed
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance - $1 WHERE id = $2
`, amount, fromID)
if err != nil {
return err
}
_, err = tx.Exec(ctx, `
UPDATE accounts SET balance = balance + $1 WHERE id = $2
`, amount, toID)
if err != nil {
return err
}
return tx.Commit()
}
The Scan method reads columns from query results into variables. Columns are mapped by position, not by name - the order of arguments to Scan must match the order of columns in your SELECT statement.
type User struct {
ID string
Email string
Name string
CreatedAt time.Time
}
// Single row with QueryRow
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name, created_at FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name, &u.CreatedAt)
if err != nil {
return nil, err
}
return &u, nil
}
// You can also scan into an inline struct
func getItem(ctx context.Context, id int64) error {
var item struct {
ID int64
Title string
Done bool
}
err := db.QueryRow(ctx, `
SELECT id, title, done FROM items WHERE id = $1
`, id).Scan(&item.ID, &item.Title, &item.Done)
return err
}
Always use parameterized queries:
// SAFE - values are parameterized
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE email = $1
`, email).Scan(&u.ID, &u.Email, &u.Name)
// WRONG - SQL injection risk
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
import (
"errors"
"encore.dev/storage/sqldb"
"encore.dev/beta/errs"
)
func getUser(ctx context.Context, id string) (*User, error) {
var u User
err := db.QueryRow(ctx, `
SELECT id, email, name FROM users WHERE id = $1
`, id).Scan(&u.ID, &u.Email, &u.Name)
if errors.Is(err, sqldb.ErrNoRows) {
return nil, &errs.Error{
Code: errs.NotFound,
Message: "user not found",
}
}
if err != nil {
return nil, err
}
return &u, nil
}
$1, $2, etc.)Scan to read query results - columns are mapped by positionsqldb.ErrNoRows when expecting a single rowWeekly Installs
121
Repository
GitHub Stars
20
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode102
gemini-cli101
codex101
claude-code94
github-copilot83
cursor79
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
116,600 周安装
Postmark API 技能指南:事务性邮件发送、入站处理、模板管理与送达率优化
210 周安装
PPTX 文件处理与自动化:创建、编辑、分析 PowerPoint 演示文稿的完整指南
215 周安装
React前端开发指南:Suspense数据获取、性能优化与TypeScript最佳实践
209 周安装
AWS DynamoDB 单表设计指南:NoSQL 数据库建模与 TypeScript/Python SDK 实战
210 周安装
iOS Swift AVFoundation 相机捕捉开发指南:自定义相机UI、照片视频录制、会话管理
210 周安装
OAuth 2.0与OpenID Connect完整实现指南:JWT令牌、安全会话与最佳实践
211 周安装