Skip to main content

Echo Routing Basics

Introduction

Echo is a high-performance, extensible, minimalist web framework for Go. At its core, Echo provides a powerful routing system that allows developers to define HTTP endpoints and handlers with ease. This guide introduces you to the basics of routing in Echo, showing you how to structure your web applications effectively.

Routing is the process of determining how an application responds to client requests to specific endpoints, which are URIs (or paths) and HTTP methods (GET, POST, PUT, DELETE, etc.). Understanding routing is fundamental to building web applications with Echo.

Getting Started with Echo

Before diving into routes, let's set up a basic Echo application:

go
package main

import (
"net/http"
"github.com/labstack/echo/v4"
)

func main() {
// Create a new Echo instance
e := echo.New()

// Start the server
e.Start(":8080")
}

This creates a minimal Echo server that runs on port 8080, but it doesn't handle any routes yet.

Defining Basic Routes

Echo allows you to define routes for different HTTP methods and paths. Here's how to create basic routes:

go
package main

import (
"net/http"
"github.com/labstack/echo/v4"
)

func main() {
e := echo.New()

// Define a route that handles GET requests to the root path
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})

e.Start(":8080")
}

When you run this code and navigate to http://localhost:8080/ in your browser, you'll see the text "Hello, World!" displayed.

HTTP Methods in Echo

Echo supports all standard HTTP methods. Here's how to define routes for different methods:

go
// GET request handler
e.GET("/users", getUsers)

// POST request handler
e.POST("/users", createUser)

// PUT request handler
e.PUT("/users/:id", updateUser)

// DELETE request handler
e.DELETE("/users/:id", deleteUser)

// PATCH request handler
e.PATCH("/users/:id", partialUpdateUser)

Route Parameters

Often, you need to capture parts of the URL as parameters. Echo makes this easy:

go
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)
})

When a request is made to /users/123, the handler will receive 123 as the id parameter, and the response will be "User ID: 123".

Query Parameters

Query parameters appear after the ? in a URL. Echo provides a simple way to access them:

go
e.GET("/search", func(c echo.Context) error {
// Get query parameter
query := c.QueryParam("q")
return c.String(http.StatusOK, "Search query: "+query)
})

For a request to /search?q=echo+routing, the response will be "Search query: echo routing".

Group Routes

As your application grows, grouping related routes becomes essential:

go
// Create an API group
api := e.Group("/api")

// Routes within the API group
api.GET("/users", getAllUsers)
api.POST("/users", createUser)
api.GET("/users/:id", getUser)

// You can create nested groups too
v1 := api.Group("/v1")
v1.GET("/products", getProductsV1)

This organizes your routes under prefixes like /api/users and /api/v1/products.

Route Middleware

Middleware functions allow you to execute code before and after your route handlers:

go
// Logger middleware
e.Use(middleware.Logger())

// Recover middleware
e.Use(middleware.Recover())

// Group-specific middleware
admin := e.Group("/admin", middleware.BasicAuth(validateAdmin))
admin.GET("/dashboard", showDashboard)

Practical Example: Building a RESTful API

Let's put everything together to build a simple API for managing users:

go
package main

import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)

type User struct {
ID string `json:"id"`
Name string `json:"name"`
}

var users = map[string]User{}

func main() {
e := echo.New()

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Welcome to the User API!")
})

// User routes
api := e.Group("/api")
api.GET("/users", getAllUsers)
api.POST("/users", createUser)
api.GET("/users/:id", getUser)
api.PUT("/users/:id", updateUser)
api.DELETE("/users/:id", deleteUser)

// Start server
e.Start(":8080")
}

// Handler functions
func getAllUsers(c echo.Context) error {
// Convert map to slice for JSON response
userList := make([]User, 0, len(users))
for _, user := range users {
userList = append(userList, user)
}
return c.JSON(http.StatusOK, userList)
}

func createUser(c echo.Context) error {
user := new(User)
if err := c.Bind(user); err != nil {
return err
}
users[user.ID] = *user
return c.JSON(http.StatusCreated, user)
}

func getUser(c echo.Context) error {
id := c.Param("id")
user, exists := users[id]
if !exists {
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}
return c.JSON(http.StatusOK, user)
}

func updateUser(c echo.Context) error {
id := c.Param("id")
if _, exists := users[id]; !exists {
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}

user := new(User)
if err := c.Bind(user); err != nil {
return err
}

user.ID = id
users[id] = *user
return c.JSON(http.StatusOK, user)
}

func deleteUser(c echo.Context) error {
id := c.Param("id")
if _, exists := users[id]; !exists {
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}

delete(users, id)
return c.NoContent(http.StatusNoContent)
}

Testing Your Routes

You can test this API with curl commands:

bash
# Create a new user
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"id":"1", "name":"John Doe"}'

# Get all users
curl http://localhost:8080/api/users

# Get a specific user
curl http://localhost:8080/api/users/1

# Update a user
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"id":"1", "name":"John Updated"}'

# Delete a user
curl -X DELETE http://localhost:8080/api/users/1

Named Routes

Echo allows you to name your routes and then generate URLs for these named routes:

go
e.GET("/users/:id", getUser).Name = "get-user"

// Later, you can generate URLs for this route
url := e.Reverse("get-user", "123") // "/users/123"

Route Matching Priority

Echo uses the following rules to match routes:

  1. Static routes have the highest priority (/users/new)
  2. Param routes have mid priority (/users/:id)
  3. Match-any routes have the lowest priority (/users/*)
go
// This route has higher priority
e.GET("/users/new", newUser)

// This route has lower priority
e.GET("/users/:id", getUser)

Summary

In this guide, we've explored the basics of Echo routing, including:

  • Setting up a basic Echo server
  • Defining routes for various HTTP methods
  • Working with path and query parameters
  • Grouping routes for better organization
  • Applying middleware to routes
  • Building a complete RESTful API with Echo

Echo's routing system provides a clean and powerful way to structure your web applications while maintaining high performance and readability.

Additional Resources and Exercises

Resources

Exercises

  1. Basic Route Exercise: Create an Echo application with routes to display different HTML pages (home, about, contact).

  2. Parameter Handling: Build a route that accepts multiple parameters (e.g., /products/:category/:id) and displays information based on them.

  3. Middleware Practice: Create a custom middleware that logs the time taken to process each request.

  4. Complete CRUD API: Extend the user API example to include validation, persistence, and additional fields for users.

  5. Authentication Routes: Implement user registration and login routes with JWT token generation.

By mastering Echo's routing basics, you've taken the first step toward building robust, high-performance web applications in Go.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)