Learn Echo - Core Concepts & Main Architecture
Series/Learn Echo/Episode 2
Episode 2 of 23

Learn Echo - Core Concepts & Main Architecture

This episode dissects Echo's architecture from the inside: how echo.Echo becomes a net/http handler, how the radix tree router matches paths, the execution order of the middleware chain, and the role of echo.Context that wraps request and response in every handler.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

Before writing code, you need to understand what happens behind the scenes. Echo is often called a "web framework", but it's really a thin layer that orchestrates how standard HTTP requests are processed. Understanding this architecture will make debugging much easier throughout the series.

Episode 2 dissects Echo's architecture from the inside: how an echo.Echo instance becomes a net/http handler, how the radix tree router matches paths, how the middleware chain is executed at root, group, and route levels, and the role of echo.Context that wraps request and response.

echo.Echo and the Request Cycle

From Incoming Request to Handler

The flow of a request in Echo is simple but precise:

  • The net/http server receives the request and calls the Echo handler.
  • The radix tree router matches the request path against registered routes.
  • The relevant middleware chain executes in order.
  • The final handler is called with a ready-to-use echo.Context.
  • The response is written, and control flows back up through the middleware.

In Echo v5, the handler signature is func(echo.Context) error. Note that in v5 echo.Context is always a pointer *echo.Context, an important change from v4 that we'll discuss in episode 20.

Echo v5 handler structure
func handler(c echo.Context) error {
    return c.String(200, "selamat datang di Echo")
}

The handler receives a context, does something, then returns an error. The function handler(c echo.Context) error is the basic contract of every Echo application — there are no hidden panics, all failures flow as errors.

Radix Tree Router and Path Matching

One Pass, Many Patterns

The core of Echo's performance is the router. The router uses a radix tree — a compressed trie — where each node represents a path segment. Matching is done per character in a single pass, not by iterating over a list of routes.

Each node stores the supported HTTP methods, the handler, and middleware metadata. When a route is registered, Echo inserts it into the tree according to the path pattern. That's why e.GET("/users/:id") and e.GET("/users/:id/posts") don't collide — they're different nodes on the same branch.

Registering routes in the router
e := echo.New()
e.GET("/users/:id", getUser)
e.GET("/users/:id/posts", getPosts)

When a request to /users/42 comes in, the router traverses the tree, finds the :id node, and fills in the parameter value 42. The handler retrieves this value via c.Param("id").

Automatic Route Priority

One of Echo's strengths: automatic route priority. If you register /users/new and /users/:id, the static new route takes precedence when a request comes in for /users/new — no special registration order needed. This differs from some other frameworks that depend on declaration order.

The Middleware Chain

Nested Execution Order

Echo middleware is arranged like layers of an onion. Execution starts from the outermost middleware, descends through c.Next(), reaches the handler, then climbs back up. Where you attach middleware determines its position in the chain:

  • e.Pre: middleware before routing, runs first.
  • e.Use: middleware at the root level, applies to all routes.
  • group.Use: middleware specific to a group.
  • Route-specific: middleware attached directly to a single route.
Middleware at various levels
e := echo.New()
e.Pre(middleware.RemoveTrailingSlash())
e.Use(middleware.Logger())
api := e.Group("/api", middleware.CORS())
api.GET("/health", healthHandler)

In the code above, RemoveTrailingSlash runs before routing, Logger for all requests, and CORS only for routes inside the /api group. This is a pattern you'll use over and over.

echo.Context: The Bridge Between Request and Response

The Context API in v5

echo.Context is the object you'll touch most often. It wraps the request (c.Request()), the response (c.Response()), route parameters, query, and data stored inside it.

In Echo v5, the context is a pointer *echo.Context, which makes it lighter to pass around and store. The context provides a set of methods that will accompany you throughout the series:

The most commonly used context methods
name := c.Param("name")
q := c.QueryParam("q")
body := new(User)
if err := c.Bind(body); err != nil {
    return err
}
return c.JSON(200, body)

Methods like c.Param, c.QueryParam, c.Bind, and c.JSON are the most widely used parts of the API surface — their details will be broken down one by one in episodes 4, 5, and 7.

Echo's Core Components

Eight Objects That Always Interact

Echo's architecture can be summarized in the following components:

  • Echo: the main instance that holds configuration and stores the router.
  • Router: the radix tree that matches requests to handlers.
  • Context: wraps request and response for a single request.
  • Handler: a func(echo.Context) error function containing the application logic.
  • Middleware: functions that wrap handlers for cross-cutting concerns.
  • Binder: converts the request body into a Go struct.
  • Validator: validates structs after binding.
  • HTTPError: a centralized error representation with a status code and message.

All of these components can be replaced or customized. The binder, validator, and renderer are pluggable — you'll take advantage of this starting in episode 5.

Continue the project from episode 0
cd belajar-echo
go mod tidy

Run go mod tidy to clean up dependencies before writing code in the next episode.

Closing

Episode 2 lifted the veil on Echo's architecture: echo.Echo is a net/http handler, the radix tree router matches paths in a single pass with automatic priority, middleware is arranged in layers at pre, root, group, and route levels, and echo.Context is the main bridge between request, response, and handler.

Key takeaways:

  • echo.Echo is wrapped into a standard net/http handler.
  • The router uses a radix tree to match paths efficiently.
  • Static routes always take precedence over parameter routes.
  • Middleware executes like layers of an onion, starting from the outermost.
  • e.Pre, e.Use, group.Use, and route middleware have different scopes.
  • In v5, echo.Context is a pointer *echo.Context.
  • The binder, validator, and renderer are pluggable.

In the next episode we'll build setup & hello world — your first Echo project with echo.New(), the RequestLogger middleware based on slog and Recover, your first handler that returns text and JSON, and running the server on port 8080. Preparation is done, time to write real code!