Library / SDK
google/go-querystring avatar
google/go-querystring

go-querystring: building URL query parameters from a typed struct

go-querystring is Go library for encoding structs into URL query strings.

2,146 stars191 forksGoBSD-3-Clause

At a glance

What is it?
A small Go library with a single exported function that turns a struct into URL query parameters using url tags, so a request's parameters are type checked by the compiler instead of assembled from strings.
Who is it for?
go-querystring earns a place in any Go client that builds URLs from typed options, because it replaces string concatenation with a struct the compiler checks. Its scope is deliberately narrow: one exported function, encoding only, and everything about supported types and formatting lives in the package documentation rather than the README.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 48 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One exported function and one struct tag

The import is the whole API surface:

go
import "github.com/google/go-querystring/query"

The README is explicit that the query package exports a single `Values()` function, and that is the entire surface. Everything the library does, it does through one call that takes a struct and returns encoded URL values.

The README's example is small enough to hold in your head. An options struct carries three fields, each with a `url` tag naming the query parameter it maps to, and the call turns that struct into a string:

go
type Options struct {
  Query   string `url:"q"`
  ShowAll bool   `url:"all"`
  Page    int    `url:"page"`
}

opt := Options{ "foo", true, 2 }
v, _ := query.Values(opt)
fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"

The result is a `url.Values`, which is why the example calls `.Encode()` on it rather than printing a string directly. That detail matters when you write your own code, because you get a structured value back and decide yourself whether to attach it to a request, append it to an existing URL, or inspect individual keys. The bool becomes `all=true` rather than being omitted, which is the behavior you want for a flag that defaults to false in the server but is being sent explicitly.

The problem it solves is type safety, not convenience

The README frames the purpose carefully. go-querystring is designed for situations where you want to construct a URL using a struct that represents the query parameters, and the stated reason is enforcing type safety of those parameters. The library it names as doing this already is `go-github`, linked at a specific commit in the README.

That reference is the most useful line in the README, because it points at a real, widely used client rather than a hypothetical. The pattern it describes is the ordinary Go way of handling this: define an options struct, give each field a tag naming its wire representation, let a library do the encoding, and hand the struct around as a value. Because `Options` is a type, a change to a field name is a compile error in every caller, and a change to a field's type is caught at the same place.

The alternative, which is still common in Go code, is building the query by hand with `url.Values` and string keys. That works and it is what the standard library offers directly. What it lacks is the connection between the Go-side name and the wire name, so a rename or a typo becomes a runtime 400 rather than a build failure. For a client library where parameters get added and renamed over years, that is a real class of bug, and it is the class this package removes.

The tradeoff is that the struct tag is now part of your public API. Renaming a Go field no longer requires only a rename, because the tag string may also need updating, and nothing checks that the two stay in agreement. That is the cost of the indirection, and it is small.

What the README documents and what it defers to the godocs

This README is short, and by the standards of the ecosystem it is more honest about its own limits than most. It shows the import, one complete example, and then says to see the package godocs for complete documentation on supported types and formatting options.

That sentence is doing real work, and you should read it as an instruction rather than a formality. The README does not enumerate which types are supported, how nested structs are treated, how slices and maps are encoded, how pointers behave, or what optional tag values exist beyond the plain name. If you are encoding a flat struct of strings, ints and bools, the README is genuinely sufficient. If you are encoding a nested struct or a list of filters, the godocs are where the answer is, and guessing from the example will not get you there.

The repository structure is consistent with that split. The top level holds `.github/`, `.gitignore`, `.golangci.yml`, `CONTRIBUTING.md`, `LICENSE`, `README.md`, `go.mod` and `go.sum`, and the only code is under `query/`. There is no `examples/` directory and no separate documentation tree, so the package documentation on pkg.go.dev is the reference. The README also links its own badge to that page, which is a small signal about where the project considers its documentation to live.

Two details in the README are worth noting as documentation rather than criticism. The project description on the repository reads that go-querystring is a Go library for encoding structs into URL query strings, and the README's own first line after the badges drops the word a, reading instead that go-querystring is Go library for encoding structs into URL query parameters. It is a missing article, harmless in practice, and a reminder that this file has been edited lightly for years.

A very old Go directive and a single test dependency

`go.mod` is four lines and worth reading closely:

text
module github.com/google/go-querystring

go 1.13

The minimum Go version is 1.13, which is old enough to be a feature rather than neglect. A library with no dependencies can keep a low floor indefinitely, and declaring 1.13 means it will build inside almost any module graph, including one pinned by an older application. Compare that with the current state of the ecosystem, where a Go module can require a recent toolchain and refuse to build without it. This package chose the floor.

The dependency list is short: `github.com/google/go-cmp v0.6.0` and nothing else. That is a test-only dependency, the comparison library appearing as the way the package's own tests check encoder output. There is no runtime dependency at all, so importing it adds no transitive surface to your binary. The `.golangci.yml` at the top level indicates the project lints itself, and `go.sum` exists even with a single requirement.

The only release in the repository's history is `v1.2.0`, published 2025-12-30. Its notes are candid about what happened: a lot of version bumps, mostly of GitHub Actions with nothing to do with the package, plus two minor code optimizations. One prioritizes handling boundary conditions, and the other replaces `bytes.Buffer` with `strings.Builder`. Both are internal performance and correctness refinements with no API change, which is what you want to see in a package whose whole purpose is to be boring and dependable.

The last push was on 2026-09-08 for the tags area, with the branch itself last pushed on 2026-08-20. The license is BSD 3-Clause and the repository is not archived.

Encoding only, and the four alternatives the README lists

The README closes with an alternatives section, and its framing is precise. If you need a library that can both encode and decode query strings, it points at a short list: gorilla/schema, pasztorpisti/qs, hetiansu5/urlquery, and ggicci/httpin, which it marks as decoder only.

That list is an accurate description of the gap. go-querystring encodes a struct into query parameters, and the search terms people pair with it show the other half of the problem coming up constantly: parsing query params back into a struct is a separate need, and this package does not address it. If your work is a client that only makes requests, encoding is all you need and this is a good choice. If you also parse inbound requests, you need a second library, and the README hands you four candidates rather than one opinion.

The distinction between those alternatives is worth understanding before choosing. A library that does both means one set of tag conventions for encoding and decoding, so a struct stays the single description of your parameters in both directions. Two single-purpose libraries mean two conventions, and a mismatch between them shows up as parameters that serialize but do not parse, which is a slow bug. The decoder-only option in the list is the one to skip if you need symmetry, since it leaves the encoding half to something else by definition.

Compared with the standard library, the trade is tags against manual assembly. `url.Values` gives you escaping, ordering and encoding with no dependency and no struct tags, and for a request with three fixed parameters it is entirely adequate. The case for a struct grows with the number of parameters, with the number of call sites, and with how often those parameters get renamed. That is the same calculus the `go-github` reference in the README describes.

Where the design will frustrate you

Two limitations are structural rather than fixable, and both follow from keeping the package to one function and one tag.

The first is the decoding gap described above. If your codebase needs both directions, you are adding two dependencies where one might do, or writing the decode by hand with reflection and accepting the same risks you were trying to remove. Neither is wrong, but it is a decision to make deliberately rather than discover later.

The second is that struct tags are strings, so the compiler cannot catch a mismatch between the Go field name and the wire name. Rename a field and forget the tag, and the request silently changes shape. Nothing fails to build, and the server decides what happens next. Keeping the struct and its wire representation in one file helps, and so does treating a rename as a two-part edit, but the package cannot enforce either.

There is also a practical note about what the encoder returns. Because the result is `url.Values`, ordering follows the struct's field order rather than anything semantic, and repeated parameters come from slice encoding in a way the README does not show. If you are matching a request against a recorded fixture or a checksum, that ordering is a detail worth confirming against your endpoint rather than assuming.

None of this makes the package a poor choice. A single function with no dependencies, a low Go floor, no API changes in its most recent release and a reference implementation in a widely used client is about as low-risk a dependency as Go has. It is simply a small tool, and the godocs rather than the README are where its real behavior is specified.

Editorial conclusion

go-querystring earns a place in any Go client that builds URLs from typed options, because it replaces string concatenation with a struct the compiler checks. Its scope is deliberately narrow: one exported function, encoding only, and everything about supported types and formatting lives in the package documentation rather than the README. That makes it a poor fit if you also need to decode a query string into a struct, where the README points you at gorilla/schema, pasztorpisti/qs, hetiansu5/urlquery or the decoder-only ggicci/httpin. Read the godocs for the `url` tag options before you rely on it for anything with nested structs or slices, then try the `Options` example against a real endpoint to see the exact encoding your API expects.

Frequently asked questions

What does go-querystring do?

It encodes a Go struct into URL query parameters. The query package exports a single `Values()` function that reads `url` struct tags to map fields to parameter names and returns `url.Values` you can encode onto a request.

How do I use go-querystring in a Go program?

Import `github.com/google/go-querystring/query`, define a struct with `url` tags on each field, then call `query.Values(opt)` and use the returned value's `Encode` method. The README's `Options` example with a string, a bool and an int encodes to `q=foo&all=true&page=2`.

Can go-querystring parse a query string back into a struct?

No. It encodes only. The README points to gorilla/schema, pasztorpisti/qs and hetiansu5/urlquery for libraries that do both encode and decode, and to ggicci/httpin as a decoder-only option.

What Go version and dependencies does go-querystring need?

Its `go.mod` declares Go 1.13 as the minimum, and the only requirement is `google/go-cmp`, which is used by the tests. The package has no runtime dependencies.

Official sources

  1. google/go-querystring on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/google-go-querystring.svg)](https://hysenlabs.com/projects/google-go-querystring)