Echo Query Parameters
Introduction
When building web applications with the Echo framework, you'll often need to extract and process data from URL query parameters. Query parameters are key-value pairs that appear after a question mark (?
) in a URL, such as https://example.com/search?query=golang&page=2
. They provide a way for clients to send data to your server without using a request body.
In this guide, you'll learn:
- What query parameters are and how they work
- How to access query parameters in Echo
- Different methods for handling query parameters
- Real-world examples and best practices
Understanding Query Parameters
Query parameters are a standard way to pass information through a URL. They follow a specific format:
- The parameters begin with a question mark (
?
) - Each parameter is a key-value pair formatted as
key=value
- Multiple parameters are separated by an ampersand (
&
)
For example:
https://api.example.com/products?category=electronics&sort=price_asc&limit=20
In this URL, we have three query parameters:
category
with valueelectronics
sort
with valueprice_asc
limit
with value20
Accessing Query Parameters in Echo
Echo makes it easy to access query parameters through its Context
object. Let's look at the main methods:
Using c.QueryParam()
The most straightforward way to retrieve a query parameter is to use the QueryParam()
method:
// Handler function
func getProducts(c echo.Context) error {
// Get query parameters
category := c.QueryParam("category")
sort := c.QueryParam("sort")
// Use the values
return c.String(http.StatusOK, fmt.Sprintf("Category: %s, Sort: %s", category, sort))
}
When a request is made to /products?category=electronics&sort=price_asc
, this handler would respond with:
Category: electronics, Sort: price_asc
Using c.QueryParams()
If you need to access all query parameters at once, Echo provides the QueryParams()
method that returns a map of parameter names to values:
func getAllParams(c echo.Context) error {
// Get all query parameters
params := c.QueryParams()
// Build a response
var response strings.Builder
for key, values := range params {
for _, value := range values {
response.WriteString(fmt.Sprintf("%s: %s\n", key, value))
}
}
return c.String(http.StatusOK, response.String())
}
For a request to /search?term=golang&category=programming&category=language
, this would respond with:
term: golang
category: programming
category: language
Handling Multiple Values
Note that query parameters can have multiple values with the same key. For example, /search?tag=go&tag=web
has two values for the tag
parameter. To access all values for a specific parameter, use c.QueryParams().Get(key)
:
func getTags(c echo.Context) error {
// Get all values for the "tag" parameter
tags := c.QueryParams()["tag"]
return c.JSON(http.StatusOK, map[string]interface{}{
"tags": tags,
})
}
Type Conversion
Query parameters are always received as strings. If you need them in other types, you'll need to convert them:
func getProductsWithPagination(c echo.Context) error {
// Get query parameters
pageStr := c.QueryParam("page")
limitStr := c.QueryParam("limit")
// Convert to integers with default values
page := 1
limit := 10
if pageStr != "" {
if pageVal, err := strconv.Atoi(pageStr); err == nil && pageVal > 0 {
page = pageVal
}
}
if limitStr != "" {
if limitVal, err := strconv.Atoi(limitStr); err == nil && limitVal > 0 {
limit = limitVal
}
}
return c.JSON(http.StatusOK, map[string]interface{}{
"page": page,
"limit": limit,
"offset": (page - 1) * limit,
})
}
Validating Query Parameters
It's important to validate query parameters to ensure the data meets your requirements. Here's a simple example of validating a sorting parameter:
func getProductsWithSorting(c echo.Context) error {
// Get and validate the sort parameter
sort := c.QueryParam("sort")
// Validate sort parameter
validSortOptions := map[string]bool{
"price_asc": true,
"price_desc": true,
"name_asc": true,
"name_desc": true,
}
if sort != "" && !validSortOptions[sort] {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid sort parameter. Valid options are: price_asc, price_desc, name_asc, name_desc",
})
}
// Default sort if not provided
if sort == "" {
sort = "name_asc"
}
return c.JSON(http.StatusOK, map[string]string{
"sorting": sort,
})
}
Real-World Example: Product Search API
Let's build a more comprehensive example of a product search API that uses multiple query parameters:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
Price float64 `json:"price"`
}
// Sample product database
var products = []Product{
{1, "Laptop", "electronics", 999.99},
{2, "Smartphone", "electronics", 599.99},
{3, "Coffee Maker", "kitchen", 89.99},
{4, "Running Shoes", "sports", 129.99},
{5, "Wireless Earbuds", "electronics", 149.99},
}
func searchProducts(c echo.Context) error {
// Get query parameters
category := c.QueryParam("category")
minPriceStr := c.QueryParam("min_price")
maxPriceStr := c.QueryParam("max_price")
// Parse price filters
var minPrice, maxPrice float64
var err error
if minPriceStr != "" {
minPrice, err = strconv.ParseFloat(minPriceStr, 64)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid min_price parameter",
})
}
}
if maxPriceStr != "" {
maxPrice, err = strconv.ParseFloat(maxPriceStr, 64)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid max_price parameter",
})
}
}
// Filter products based on parameters
filteredProducts := []Product{}
for _, product := range products {
// Filter by category if specified
if category != "" && product.Category != category {
continue
}
// Filter by min price if specified
if minPriceStr != "" && product.Price < minPrice {
continue
}
// Filter by max price if specified
if maxPriceStr != "" && product.Price > maxPrice {
continue
}
// Product passed all filters, add it to results
filteredProducts = append(filteredProducts, product)
}
return c.JSON(http.StatusOK, map[string]interface{}{
"products": filteredProducts,
"count": len(filteredProducts),
"filters": map[string]interface{}{
"category": category,
"min_price": minPrice,
"max_price": maxPrice,
},
})
}
// Register route
func registerRoutes(e *echo.Echo) {
e.GET("/products", searchProducts)
}
When making a request to /products?category=electronics&min_price=200
, you'll get only electronics products priced at $200 or higher.
Advanced Techniques: Query Parameter Binding
For more complex APIs, Echo provides a convenient way to bind query parameters to structs using the c.Bind()
method:
type SearchParams struct {
Category string `query:"category"`
MinPrice float64 `query:"min_price"`
MaxPrice float64 `query:"max_price"`
Sort string `query:"sort" default:"name_asc"`
Page int `query:"page" default:"1"`
Limit int `query:"limit" default:"10"`
}
func searchProductsWithBinding(c echo.Context) error {
// Create and bind parameters
params := new(SearchParams)
if err := c.Bind(params); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid parameters",
})
}
// Now you can use params.Category, params.MinPrice, etc.
// Apply filtering logic...
return c.JSON(http.StatusOK, map[string]interface{}{
"params": params,
// other response data
})
}
Best Practices
- Always validate query parameters: Don't assume users will send valid data.
- Provide default values: For optional parameters, define sensible defaults.
- Use appropriate data types: Convert string parameters to appropriate types (int, float, bool).
- Handle multiple values when appropriate (e.g., for filtering by multiple categories).
- Document your API parameters: Make it clear what parameters your API accepts.
- Use parameter binding for complex APIs with many parameters.
Summary
Query parameters provide a flexible way to pass data to your Echo web applications. In this guide, we've covered:
- How to access individual and multiple query parameters
- Converting parameter values to appropriate types
- Validating parameters to ensure data integrity
- Building real-world examples that use query parameters
- Advanced techniques like parameter binding
By mastering query parameters, you can build flexible, user-friendly APIs that allow clients to customize their requests exactly as needed.
Exercises
- Build a simple weather API that accepts
city
andunits
query parameters (where units can be "metric" or "imperial"). - Create a blog post search endpoint that allows filtering by tags, category, and date range using query parameters.
- Implement a product filter using multiple values for the same parameter (e.g.,
color=red&color=blue
). - Use parameter binding to create a complex search API with pagination, sorting, and filtering options.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)