Admin Edit Form
This is the pattern for a back-office form: several optional fields, a few values that can be intentionally cleared, and a returning clause so the UI can refresh from the database instead of guessing.
The form payload
type AdminUserForm struct {
ID int
DisplayName *string
Email *string
Bio *string
Enabled *bool
TeamID *int
}
The important bit is that the form uses pointers for fields that may be omitted. A nil pointer means “leave it alone.” A non-nil pointer means “the user actually submitted something.”
Apply the patch
func UpdateUser(ctx context.Context, db scan.Queryer, qq *quarry.Quarry, form AdminUserForm) (*AdminUserRow, error) {
q := qq.Update("users").
SetOptional("display_name", form.DisplayName).
SetOptional("email", form.Email).
SetOptional("bio", form.Bio).
SetIf(form.Enabled != nil, "enabled", *form.Enabled).
SetIf(form.TeamID != nil, "team_id", *form.TeamID).
Where(quarry.Eq("id", form.ID)).
Returning("id", "display_name", "email", "bio", "enabled", "team_id")
return scan.One[AdminUserRow](ctx, db, q)
}
When a field must be clearable
If the UI needs to clear a value, send an explicit nil-or-empty signal. In this sample,
DisplayName and Bio can be cleared by sending an empty string
through a pointer field and handling that logic before the query builder runs.
func normalizeForm(form *AdminUserForm) {
if form.DisplayName != nil && *form.DisplayName == "" {
empty := ""
form.DisplayName = &empty
}
}
The point is not to compress everything into one helper. The point is to keep the SQL assembly boring and move intent handling into the request layer where it belongs.
Map-driven admin toggles
q := qq.Update("users").
SetMap(map[string]any{
"role": "moderator",
"locked": false,
"updated_at": time.Now(),
}).
Where(quarry.Eq("id", form.ID)).
Returning("id", "role", "locked")
SetMap is handy when a form or admin tool already has stable keys and you
want the update builder to keep the SET order deterministic.
What the SQL looks like
UPDATE users
SET display_name = $1, email = $2, enabled = $3, team_id = $4
WHERE id = $5
RETURNING id, display_name, email, bio, enabled, team_id