Skip to main content

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:

  1. Path (Route) Parameters - embedded in the URL path itself (/users/:id)
  2. 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

go
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:

go
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:

go
// 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

go
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:

go
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:

go
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:

go
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:

go
import (
"strconv"
// other imports...
)

Binding Parameters to Structs

For more complex APIs, you can bind query parameters directly to structs:

go
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:

go
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:

  1. Using path parameters to identify resources (/products/:id)
  2. Using path parameters with additional context (/categories/:category/products)
  3. Using query parameters for filtering, sorting, and pagination
  4. Binding query parameters to structs for easier processing
  5. Setting default values for optional parameters

Best Practices for URL Parameters

When working with URL parameters in Echo:

  1. Use Path Parameters for Resources: Use path parameters to identify specific resources, like /products/:id or /users/:username.

  2. Use Query Parameters for Filtering/Sorting: Use query parameters for operations that filter, sort, or paginate results, like /products?category=electronics&sort=price.

  3. Validate Input: Always validate and sanitize parameter values, especially when they're used in database queries.

  4. Provide Defaults: Set sensible defaults for optional parameters.

  5. Use Binding for Complex Queries: For endpoints with many optional parameters, use struct binding to simplify your code.

  6. 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() and c.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

  1. Build a simple Echo API that returns a list of books with filtering by author, genre, and publication year using query parameters.

  2. Create an endpoint that accepts multiple path parameters to navigate a hierarchical structure (e.g., /api/departments/:dept/teams/:team/members/:member).

  3. Implement parameter validation for a user registration endpoint that checks that query parameters match certain formats (e.g., email, password complexity).

  4. Create an endpoint that handles pagination with page and per_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! :)