You're reading a sample of this book. Grab the full version here.

2.6. URL Query Strings

While we're on the subject of routing, let's update the showSnippet handler so that it accepts an id query string parameter from the user like so:

Method Pattern Handler Action
ANY / home Display the home page
ANY /snippet?id=1 showSnippet Display a specific snippet
POST /snippet/create createSnippet Create a new snippet

Later we'll use this id parameter to select a specific snippet from a database and show it the user. But for now, we'll just read the value of the id parameter and interpolate it with a placeholder response.

To make this work we'll need to update the showSnippet handler function to do two things:

  1. It needs to retrieve the value of the id parameter from the URL query string, which we can do using the r.URL.Query().Get() method. This will always return a string value for a parameter, or the empty string "" if no matching parameter exists.

  2. Because the id parameter is untrusted user input, we should validate it to make sure it's sane and sensible. For the purpose of our Snippetbox application, we want to check that it contains a positive integer value. We can do this by trying to convert the string value to an integer with the strconv.Atoi() function, and then checking the value is greater than zero.

Here's how:

File: main.go
package main

import (
    "fmt" // New import
    "strconv" // New import


func showSnippet(w http.ResponseWriter, r *http.Request) {
    // Extract the value of the id parameter from the query string and try to
    // convert it to an integer using the strconv.Atoi() function. If it can't
    // be converted to an integer, or the value is less than 1, we return a 404 page
    // not found response.
    id, err := strconv.Atoi(r.URL.Query().Get("id"))
    if err != nil || id < 1 {
        http.NotFound(w, r)

    // Use the fmt.Fprintf() function to interpolate the id value with our response
    // and write it to the http.ResponseWriter.
    fmt.Fprintf(w, "Display a specific snippet with ID %d...", id)


Let's try this out.

Restart the application, and try visiting a URL like http://localhost:4000/snippet?id=123. You should see a response which looks like this:


You might also like to try visiting some URLs which have invalid values for the id parameter, or no parameter at all. For instance:

For all these requests you should get a 404 page not found response.

The io.Writer Interface

The code above introduced another new thing behind-the-scenes. If you take a look at the documentation for the fmt.Fprintf() function you'll notice that it takes an io.Writer as the first parameter...

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

...but we passed it our http.ResponseWriter object instead — and it worked fine.

We're able to do this because the io.Writer type is an interface, and the http.ResponseWriter object satisfies the interface because it has a w.Write() method.

If you're new to Go, then the concept of interfaces can be a bit confusing and I don't want to get too hung up on it right now. It's enough to know that — in practice — anywhere you see an io.Writer parameter it's OK to pass in your http.RespsonseWriter object. Whatever is being written will subsequently be sent as the body of the HTTP response.