Not sure how to structure your Go web application?

My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.

Take a look!

Quick tip: Change URL query params in Go

Published on:

In this short post we're going to discuss how to add, modify or delete URL query string parameters in Go. To illustrate, we'll look at how to change this URL:

https://example.com?name=alice&age=28&gender=female

To this:

https://example.com?name=alice&age=29&occupation=carpenter

If you want to change the URL query string in place:

// Use url.Parse() to parse a string into a *url.URL type. If your URL is
// already a url.URL type you can skip this step.
urlA, err := url.Parse("https://example.com?name=alice&age=28&gender=female")
if err != nil {
    log.Fatal(err)
}

// Use the Query() method to get the query string params as a url.Values map.
values := urlA.Query()

// Make the changes that you want using the Add(), Set() and Del() methods. If
// you want to retrieve or check for a specific parameter you can use the Get()
// and Has() methods respectively. 
values.Add("occupation", "carpenter")
values.Del("gender")
values.Set("age", strconv.Itoa(29))

// Use the Encode() method to transform the url.Values map into a URL-encoded
// string (like "age=29&name=alice...") and assign it back to the URL. Note 
// that the encoded values will be sorted alphabetically based on the parameter 
// name. 
urlA.RawQuery = values.Encode()

fmt.Printf("urlA: %s", urlA.String())

Running this will print out:

urlA: https://example.com?age=29&name=alice&occupation=carpenter

If you want to create a clone of the URL but with a different query string, while leaving the original URL unchanged, you need to create a copy of the original url.URL struct first.

There are a couple of ways to do this. You can either re-parse the URL, or you can dereference the original url.URL and make a copy, like so:

// This is equivalent to: var newUrl url.URL = *originalUrl
newUrl := *originalUrl

When you do this, you create a new newURL variable of type url.URL which is initialized to the (dereferenced) value of *originalURL. This means that newURL has a different address in memory to originalURL.

Putting this together, the pattern for creating a new URL with different parameters is:

urlA, err := url.Parse("https://example.com?name=alice&age=28&gender=female")
if err != nil {
    log.Fatal(err)
}

// Make a copy of the original url.URL.
urlB := *urlA

// Make the param changes to the new url.URL type...
values := urlB.Query()

values.Add("occupation", "carpenter")
values.Del("gender")
values.Set("age", strconv.Itoa(29))

urlB.RawQuery = values.Encode()

fmt.Printf("urlA: %s\n", urlA.String()) // This will be unchanged.
fmt.Printf("urlB: %s\n", urlB.String()) // This will have the new params.

Running this will print out:

urlA: https://example.com?name=alice&age=28&gender=female
urlB: https://example.com?age=29&name=alice&occupation=carpenter

As a side note, you can use this technique any time you want to 'clone' a URL and make changes to it. For example to create a clone of a URL with a different path, you can do this:

urlA, err := url.Parse("https://example.com/foo")
if err != nil {
    log.Fatal(err)
}

urlB := *urlA

urlB.Path = "/bar"

fmt.Printf("%s\n", urlA.String()) // Prints https://example.com/foo
fmt.Printf("%s\n", urlB.String()) // Prints https://example.com/bar