Echo URL Parameters
URL parameters are a fundamental concept in web development that allow you to pass data between clients and servers through the URL itself. In this guide, we'll explore how the Echo framework handles URL parameters, providing you with the knowledge to build dynamic and flexible web applications.
Introduction to URL Parameters
URL parameters come in two main varieties in web applications:
- Path (Route) Parameters - embedded in the URL path itself (
/users/:id
) - Query Parameters - appended to the URL after a question mark (
/users?role=admin
)
Echo provides elegant methods to work with both types, making parameter handling intuitive and straightforward.
Path Parameters in Echo
Path parameters are defined in your route patterns using a colon (:
) followed by the parameter name.
Basic Path Parameter
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
func main() {
e := echo.New()
// Route with a path parameter
e.GET("/users/:id", func(c echo.Context) error {
// Extract the id parameter
id := c.Param("id")
return c.String(http.StatusOK, "User ID: "+id)
})
e.Start(":8080")
}
When a request comes in for /users/123
, Echo extracts 123
as the id
parameter and makes it available through the c.Param("id")
method.
Multiple Path Parameters
You can define multiple path parameters in a single route:
e.GET("/posts/:category/:id", func(c echo.Context) error {
category := c.Param("category")
id := c.Param("id")
return c.JSON(http.StatusOK, map[string]string{
"category": category,
"id": id,
})
})
A request to /posts/technology/42
would extract:
category
= "technology"id
= "42"
Path Parameter Constraints
Echo allows you to add constraints to path parameters using regular expressions:
// Only match numeric IDs
e.GET("/users/:id^[0-9]+$", func(c echo.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, "User ID (numeric): "+id)
})
This route will only match URLs like /users/123
but not /users/abc
.
Query Parameters in Echo
Query parameters appear after the ?
in a URL and are typically used for filtering, sorting, or pagination.
Basic Query Parameter
e.GET("/search", func(c echo.Context) error {
// Get query parameter
query := c.QueryParam("q")
if query == "" {
return c.String(http.StatusBadRequest, "Query parameter 'q' is required")
}
return c.String(http.StatusOK, "Searching for: "+query)
})
A request to /search?q=echo+framework
would return "Searching for: echo framework".
Multiple Query Parameters
Handling multiple query parameters is just as easy:
e.GET("/products", func(c echo.Context) error {
// Get multiple query parameters
category := c.QueryParam("category")
sortBy := c.QueryParam("sort")
page := c.QueryParam("page")
// Default values
if page == "" {
page = "1"
}
if sortBy == "" {
sortBy = "name"
}
result := map[string]string{
"category": category,
"sort": sortBy,
"page": page,
}
return c.JSON(http.StatusOK, result)
})
A request to /products?category=electronics&sort=price&page=2
would extract all three parameters.
Handling Multiple Values for the Same Parameter
Sometimes query parameters can have multiple values (e.g., /search?tag=golang&tag=web
). Echo makes this easy to handle:
e.GET("/search", func(c echo.Context) error {
// Get all values for a parameter
tags := c.QueryParams()["tag"]
return c.JSON(http.StatusOK, map[string]interface{}{
"tags": tags,
})
})
For a request to /search?tag=golang&tag=web
, this would return a JSON response with {"tags": ["golang", "web"]}
.
Type Conversion for Parameters
Echo's parameters are always string values. For common needs, you can convert them to other types:
e.GET("/items/:id", func(c echo.Context) error {
// Convert string ID to integer
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.String(http.StatusBadRequest, "Invalid ID format")
}
// Convert string query parameter to integer
limit, err := strconv.Atoi(c.QueryParam("limit"))
if err != nil || limit < 1 {
limit = 10 // Default limit
}
return c.JSON(http.StatusOK, map[string]int{
"id": id,
"limit": limit,
})
})
Don't forget to import the strconv
package:
import (
"strconv"
// other imports...
)
Binding Parameters to Structs
For more complex APIs, you can bind query parameters directly to structs:
type SearchFilters struct {
Query string `query:"q"`
Category string `query:"category"`
MinPrice int `query:"min_price"`
MaxPrice int `query:"max_price"`
Page int `query:"page"`
PerPage int `query:"per_page"`
}
e.GET("/advanced-search", func(c echo.Context) error {
filters := new(SearchFilters)
if err := c.Bind(filters); err != nil {
return err
}
// Set defaults
if filters.Page < 1 {
filters.Page = 1
}
if filters.PerPage < 1 {
filters.PerPage = 20
}
return c.JSON(http.StatusOK, filters)
})
This simplifies handling complex parameter combinations.
Real-World Example: Building a RESTful API
Let's put everything together in a more complete example of a simple product API:
package main
import (
"github.com/labstack/echo/v4"
"net/http"
"strconv"
)
// Product represents a product in our store
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
Price float64 `json:"price"`
}
// ProductQuery represents query parameters for product filtering
type ProductQuery struct {
Category string `query:"category"`
MinPrice float64 `query:"min_price"`
MaxPrice float64 `query:"max_price"`
SortBy string `query:"sort"`
Ascending bool `query:"asc"`
Page int `query:"page"`
PerPage int `query:"per_page"`
}
func main() {
e := echo.New()
// GET all products with optional filtering
e.GET("/products", listProducts)
// GET a specific product by ID
e.GET("/products/:id", getProduct)
// GET products by category
e.GET("/categories/:category/products", getProductsByCategory)
e.Start(":8080")
}
func listProducts(c echo.Context) error {
query := new(ProductQuery)
if err := c.Bind(query); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid query parameters",
})
}
// Apply defaults
if query.Page < 1 {
query.Page = 1
}
if query.PerPage < 1 || query.PerPage > 100 {
query.PerPage = 20
}
if query.SortBy == "" {
query.SortBy = "id"
}
// In a real app, you would query a database here
// For this example, we'll just return the query parameters
return c.JSON(http.StatusOK, map[string]interface{}{
"filters": query,
"page": query.Page,
"per_page": query.PerPage,
"message": "This would return filtered products",
})
}
func getProduct(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid product ID",
})
}
// In a real app, you would fetch from a database
product := Product{
ID: id,
Name: "Sample Product " + strconv.Itoa(id),
Category: "electronics",
Price: 99.99,
}
return c.JSON(http.StatusOK, product)
}
func getProductsByCategory(c echo.Context) error {
category := c.Param("category")
minPrice, _ := strconv.ParseFloat(c.QueryParam("min_price"), 64)
// In a real app, you would query a database here
return c.JSON(http.StatusOK, map[string]interface{}{
"category": category,
"min_price": minPrice,
"message": "This would return products in the category",
})
}
This example demonstrates:
- Using path parameters to identify resources (
/products/:id
) - Using path parameters with additional context (
/categories/:category/products
) - Using query parameters for filtering, sorting, and pagination
- Binding query parameters to structs for easier processing
- Setting default values for optional parameters
Best Practices for URL Parameters
When working with URL parameters in Echo:
-
Use Path Parameters for Resources: Use path parameters to identify specific resources, like
/products/:id
or/users/:username
. -
Use Query Parameters for Filtering/Sorting: Use query parameters for operations that filter, sort, or paginate results, like
/products?category=electronics&sort=price
. -
Validate Input: Always validate and sanitize parameter values, especially when they're used in database queries.
-
Provide Defaults: Set sensible defaults for optional parameters.
-
Use Binding for Complex Queries: For endpoints with many optional parameters, use struct binding to simplify your code.
-
Handle Missing Parameters Gracefully: Check whether required parameters exist and provide clear error messages.
Summary
In this guide, we've explored how to work with URL parameters in Echo:
- Path parameters define variables directly in the URL path (
/users/:id
) - Query parameters provide filtering and options (
/search?q=term
) - Echo makes both types easy to access with
c.Param()
andc.QueryParam()
- Parameters can be converted to various types, validated, and bound to structs
URL parameters are essential for building RESTful APIs and interactive web applications. Echo's parameter handling is intuitive and powerful, allowing you to create clean, maintainable code.
Additional Resources
Exercises
-
Build a simple Echo API that returns a list of books with filtering by author, genre, and publication year using query parameters.
-
Create an endpoint that accepts multiple path parameters to navigate a hierarchical structure (e.g.,
/api/departments/:dept/teams/:team/members/:member
). -
Implement parameter validation for a user registration endpoint that checks that query parameters match certain formats (e.g., email, password complexity).
-
Create an endpoint that handles pagination with
page
andper_page
query parameters, including proper validation and defaults.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)