go
66 lines · 8 steps
Building a query URL from a Filter struct in Go
A Filter struct turns optional search criteria into a safely encoded URL query string.
Explained by
highlit
1package search
2
3import (
4 "net/url"
5 "strconv"
6 "strings"
7 "time"
8)
9
10type Filter struct {
11 Query string
12 Categories []string
13 MinPrice float64
14 MaxPrice float64
15 InStock bool
16 UpdatedAt time.Time
17 Page int
18 PerPage int
19 SortBy string
20}
21
22func (f Filter) URL(base string) (string, error) {
23 u, err := url.Parse(base)
24 if err != nil {
25 return "", err
26 }
27
28 q := u.Query()
29
30 if s := strings.TrimSpace(f.Query); s != "" {
31 q.Set("q", s)
32 }
33 for _, c := range f.Categories {
34 q.Add("category", c)
35 }
36 if f.MinPrice > 0 {
37 q.Set("price_min", strconv.FormatFloat(f.MinPrice, 'f', 2, 64))
38 }
39 if f.MaxPrice > 0 {
40 q.Set("price_max", strconv.FormatFloat(f.MaxPrice, 'f', 2, 64))
41 }
42 if f.InStock {
43 q.Set("in_stock", "true")
44 }
45 if !f.UpdatedAt.IsZero() {
46 q.Set("updated_since", f.UpdatedAt.UTC().Format(time.RFC3339))
47 }
48
49 page := f.Page
50 if page < 1 {
51 page = 1
52 }
53 perPage := f.PerPage
54 if perPage < 1 || perPage > 100 {
55 perPage = 25
56 }
57 q.Set("page", strconv.Itoa(page))
58 q.Set("per_page", strconv.Itoa(perPage))
59
60 if f.SortBy != "" {
61 q.Set("sort", f.SortBy)
62 }
63
64 u.RawQuery = q.Encode()
65 return u.String(), nil
66}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Only setting query params for non-empty values keeps URLs clean and avoids sending meaningless defaults.
- 2Clamping pagination inputs to sane bounds protects downstream services from bad or hostile values.
- 3Using url.Values and Encode handles escaping for you, so never hand-concatenate query strings.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
go
package httputil import ( "net"
Safely extracting the real client IP in Go
security
http
ip-spoofing
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-query-url-from-a-filter-struct-in-go-explained-go-1c4c/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.