BuildURL and QueryInt for safe query handling

Go
package query

import (
    "fmt"
    "net/url"
    "strconv"
)

// BuildURL fills absent query keys from defaults, applies params, and encodes.
func BuildURL(rawurl string, defaults, params map[string]string) (string, error) {
    u, err := url.Parse(rawurl)
    if err != nil {
        return "", fmt.Errorf("parse url: %w", err)
    }
    q := u.Query()
    for k, v := range defaults {
        if !q.Has(k) {
            q.Set(k, v)
        }
    }
    for k, v := range params {
        q.Set(k, v)
    }
    u.RawQuery = q.Encode()
    return u.String(), nil
}

// QueryInt reads an integer query parameter, returning def when it is absent.
func QueryInt(q url.Values, key string, def int) (int, error) {
    raw := q.Get(key)
    if raw == "" {
        return def, nil
    }
    n, err := strconv.Atoi(raw)
    if err != nil {
        return 0, fmt.Errorf("query %q: %q is not an integer", key, raw)
    }
    return n, nil
}