README
ΒΆ
QS - Query String Library for Go
QS is a powerful, high-performance Go library for parsing and stringifying URL query strings with support for nested objects, arrays, and complex data structures. This library is inspired by and compatible with the popular JavaScript qs library, while providing Go-specific features like struct parsing with query tags and idiomatic Marshal/Unmarshal functions.
β¨ Features
- π Parse query strings into nested Go data structures
- π Stringify Go data structures into query strings
- ποΈ Support for nested objects and arrays with multiple formatting options
- π·οΈ Struct parsing with query tags for type-safe operations
- π Idiomatic Marshal/Unmarshal functions with automatic type detection
- π― Strapi-style API support for CMS and API applications
- β‘ High performance with comprehensive benchmarks
- π‘οΈ Extensive customization through options
- π Comprehensive documentation and examples
- π§ͺ Extensive test coverage (>95%)
- π Framework agnostic - works with any Go web framework
π¦ Installation
go get github.com/zaytracom/qs/v1
π Quick Start
Basic Usage
package main
import (
"fmt"
"log"
"github.com/zaytracom/qs/v1"
)
func main() {
// Parse a query string
result, err := qs.Parse("name=John&age=30&skills[]=Go&skills[]=Python")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)
// Output: map[age:30 name:John skills:[Go Python]]
// Stringify a data structure
data := map[string]interface{}{
"user": map[string]interface{}{
"name": "Jane",
"profile": map[string]interface{}{
"age": 25,
"skills": []interface{}{"JavaScript", "TypeScript"},
},
},
}
queryString, err := qs.Stringify(data)
if err != nil {
log.Fatal(err)
}
fmt.Println(queryString)
// Output: user[name]=Jane&user[profile][age]=25&user[profile][skills][0]=JavaScript&user[profile][skills][1]=TypeScript
}
Struct Parsing with Query Tags
type User struct {
Name string `query:"name"`
Age int `query:"age"`
Email string `query:"email"`
IsActive bool `query:"active"`
Score float64 `query:"score"`
}
func main() {
// Parse to struct
queryString := "name=John&age=30&email=john@example.com&active=true&score=95.5"
var user User
err := qs.ParseToStruct(queryString, &user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %+v\n", user)
// Convert struct to query string
newUser := User{
Name: "Alice",
Age: 25,
Email: "alice@example.com",
IsActive: true,
Score: 88.5,
}
queryString, err = qs.StructToQueryString(&newUser)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Query: %s\n", queryString)
}
Idiomatic Marshal/Unmarshal
func main() {
// Marshal with automatic type detection
user := User{Name: "John", Age: 30}
queryString, err := qs.Marshal(user)
if err != nil {
log.Fatal(err)
}
// Unmarshal with automatic type detection
var newUser User
err = qs.Unmarshal(queryString, &newUser)
if err != nil {
log.Fatal(err)
}
}
π Documentation
Core Functions
| Function | Description |
|---|---|
Parse(str, opts) |
Parse query string to map |
Stringify(obj, opts) |
Convert data structure to query string |
Marshal(v, opts) |
Convert any type to query string (idiomatic) |
Unmarshal(str, v, opts) |
Parse query string to any type (idiomatic) |
ParseToStruct(str, dest, opts) |
Parse query string to struct |
StructToQueryString(obj, opts) |
Convert struct to query string |
Array Formats
The library supports multiple array formats:
data := map[string]interface{}{
"items": []interface{}{"a", "b", "c"},
}
// Indices format (default)
qs.Stringify(data)
// Result: "items[0]=a&items[1]=b&items[2]=c"
// Brackets format
qs.Stringify(data, &qs.StringifyOptions{ArrayFormat: "brackets"})
// Result: "items[]=a&items[]=b&items[]=c"
// Repeat format
qs.Stringify(data, &qs.StringifyOptions{ArrayFormat: "repeat"})
// Result: "items=a&items=b&items=c"
Nested Objects
// Parse nested structures
result, _ := qs.Parse("user[profile][name]=John&user[profile][age]=30")
// Result: map[user:map[profile:map[age:30 name:John]]]
// Create nested structures
data := map[string]interface{}{
"user": map[string]interface{}{
"profile": map[string]interface{}{
"name": "John",
"age": 30,
},
},
}
queryString, _ := qs.Stringify(data)
// Result: "user[profile][age]=30&user[profile][name]=John"
Custom Options
Parse Options
options := &qs.ParseOptions{
Delimiter: "&", // Parameter delimiter
IgnoreQueryPrefix: true, // Ignore leading '?'
ArrayLimit: 20, // Maximum array elements
Depth: 5, // Maximum nesting depth
ParameterLimit: 1000, // Maximum parameters
}
result, err := qs.Parse("?name=John&age=30", options)
Stringify Options
options := &qs.StringifyOptions{
ArrayFormat: "brackets", // Array format
AddQueryPrefix: true, // Add '?' prefix
Delimiter: "&", // Parameter delimiter
Encode: true, // URL encoding
}
result, err := qs.Stringify(data, options)
ποΈ Advanced Usage
Strapi-style APIs
Perfect for building CMS and API applications:
type StrapiQuery struct {
Filters map[string]interface{} `query:"filters"`
Sort []string `query:"sort"`
Fields []string `query:"fields"`
Populate map[string]interface{} `query:"populate"`
Pagination map[string]interface{} `query:"pagination"`
Locale string `query:"locale"`
}
// Parse complex Strapi query
strapiQuery := "filters[title][$contains]=golang&sort[]=publishedAt:desc&populate[author][fields][]=name"
var query StrapiQuery
err := qs.Unmarshal(strapiQuery, &query)
Framework Integration
Works seamlessly with popular Go web frameworks:
Gin Framework
func getArticles(c *gin.Context) {
var query ArticleQuery
if err := qs.Unmarshal(c.Request.URL.RawQuery, &query); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
articles := getArticlesFromDB(query)
c.JSON(200, gin.H{"data": articles})
}
Echo Framework
func getProducts(c echo.Context) error {
var query ProductQuery
if err := qs.Unmarshal(c.Request().URL.RawQuery, &query); err != nil {
return echo.NewHTTPError(400, err.Error())
}
products := getProductsFromDB(query)
return c.JSON(200, products)
}
π Performance
QS is optimized for high performance:
BenchmarkParseSimple-10 169478 6458 ns/op 11499 B/op 147 allocs/op
BenchmarkParseComplex-10 64339 18474 ns/op 27625 B/op 322 allocs/op
BenchmarkStringifySimple-10 2973129 400 ns/op 190 B/op 10 allocs/op
BenchmarkStringifyComplex-10 701146 1675 ns/op 1121 B/op 31 allocs/op
BenchmarkMarshal-10 759507 1491 ns/op 520 B/op 15 allocs/op
BenchmarkUnmarshal-10 175050 7350 ns/op 3825 B/op 95 allocs/op
π§ Real-World Examples
E-commerce Product Filtering
type ProductQuery struct {
Filters struct {
Category string `query:"category"`
PriceMin float64 `query:"price_min"`
PriceMax float64 `query:"price_max"`
InStock bool `query:"in_stock"`
Brand []string `query:"brand"`
} `query:"filters"`
Sort []string `query:"sort"`
Pagination struct {
Page int `query:"page"`
PageSize int `query:"page_size"`
} `query:"pagination"`
}
// Parse: /api/products?filters[category]=electronics&filters[price_min]=100&filters[price_max]=1000&sort[]=price:asc
Search Interface
type SearchQuery struct {
Query string `query:"q"`
Filters map[string]interface{} `query:"filters"`
Facets []string `query:"facets"`
Sort []string `query:"sort"`
Page int `query:"page"`
PageSize int `query:"page_size"`
}
Analytics Dashboard
type AnalyticsQuery struct {
Metrics []string `query:"metrics"`
DateRange struct {
Start string `query:"start"`
End string `query:"end"`
} `query:"date_range"`
GroupBy []string `query:"group_by"`
Filters map[string]interface{} `query:"filters"`
}
π§ͺ Testing
Run the test suite:
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run benchmarks
go test -bench=. ./...
π€ Contributing
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
π License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
π Acknowledgments
- Inspired by the JavaScript qs library by Jordan Harband
- Thanks to all contributors who have helped improve this library
π Support
- π Documentation
- π Issue Tracker
- π¬ Discussions
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
Package qs provides parsing and stringifying of URL query strings with support for nested objects, arrays, and complex data structures.
|
Package qs provides parsing and stringifying of URL query strings with support for nested objects, arrays, and complex data structures. |
Click to show internal directories.
Click to hide internal directories.