<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sachin Chavan's blog]]></title><description><![CDATA[Sachin Chavan's blog]]></description><link>https://blog.sachinsmc.me</link><generator>RSS for Node</generator><lastBuildDate>Wed, 29 Apr 2026 20:52:40 GMT</lastBuildDate><atom:link href="https://blog.sachinsmc.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Understanding Middleware in Go with Chi]]></title><description><![CDATA[Middleware is a powerful concept in web development, allowing you to execute code before or after a request is processed by your application. In Go, middleware is commonly used to handle tasks like authentication, logging, request validation, and mor...]]></description><link>https://blog.sachinsmc.me/understanding-middleware-in-go-with-chi</link><guid isPermaLink="true">https://blog.sachinsmc.me/understanding-middleware-in-go-with-chi</guid><category><![CDATA[go-chi]]></category><category><![CDATA[golang]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[chi]]></category><category><![CDATA[Middleware]]></category><dc:creator><![CDATA[Sachin Chavan]]></dc:creator><pubDate>Sat, 24 May 2025 14:47:33 GMT</pubDate><content:encoded><![CDATA[<p>Middleware is a powerful concept in web development, allowing you to execute code before or after a request is processed by your application. In Go, middleware is commonly used to handle tasks like authentication, logging, request validation, and more. The Chi router, a lightweight and idiomatic HTTP router for Go, provides an elegant way to implement middleware. In this post, we'll explore how middleware works in Go with Chi, complete with examples.</p>
<h2 id="heading-what-is-middleware"><strong>What is Middleware?</strong></h2>
<p>Middleware in Go acts as a bridge between the HTTP request and response. It’s essentially a function that intercepts the request, performs some logic, and either passes control to the next handler or terminates the request. Middleware is often used for:</p>
<ul>
<li><p>Authentication/Authorization: Verify user credentials or tokens.</p>
</li>
<li><p>Logging: Record request details for debugging or monitoring.</p>
</li>
<li><p>Request Modification: Add headers, validate input, or modify the response.</p>
</li>
<li><p>Error Handling: Catch and handle errors gracefully.</p>
</li>
</ul>
<p>In Chi, middleware is applied to routes or groups of routes and executed in the order they are defined.</p>
<h2 id="heading-how-middleware-works-in-chi">How Middleware Works in Chi</h2>
<p>Chi is a lightweight, idiomatic router built on top of Go’s net/http package. It supports middleware through a chainable, composable approach. Middleware in Chi is implemented as an http.Handler or a function that wraps an http.Handler, allowing you to manipulate the request or response before passing it to the next handler in the chain.</p>
<p>Here’s the basic flow:</p>
<ol>
<li><p>A request hits the server.</p>
</li>
<li><p>Middleware functions are executed in the order they were registered.</p>
</li>
<li><p>Each middleware can modify the request context, terminate the request, or pass it to the next handler.</p>
</li>
<li><p>The final handler (e.g., your route handler) processes the request and generates a response.</p>
</li>
</ol>
<p>Chi provides built-in middleware for common tasks and makes it easy to write custom middleware.</p>
<h2 id="heading-writing-middleware-in-chi">Writing Middleware in Chi</h2>
<p>Let’s dive into how to create and use middleware in Chi with a practical example.</p>
<h3 id="heading-step-1-setting-up-chi">Step 1: Setting Up Chi</h3>
<p>First, install Chi:</p>
<pre><code class="lang-bash">go get github.com/go-chi/chi/v5
</code></pre>
<p>Here’s a basic Chi server setup:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    r := chi.NewRouter()

    r.Get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Hello, World!"</span>))
    })

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<h3 id="heading-step-2-creating-custom-middleware">Step 2: Creating Custom Middleware</h3>
<p>Middleware in Chi is a function that takes an http.Handler and returns an http.Handler. Here’s an example of a simple logging middleware that logs the request method and URL:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">LoggingMiddleware</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        log.Printf(<span class="hljs-string">"Request: %s %s"</span>, r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    r := chi.NewRouter()

    r.Use(LoggingMiddleware)

    r.Get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Hello, World!"</span>))
    })

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<p>In this example:</p>
<ul>
<li><p>LoggingMiddleware logs the request method and URL path.</p>
</li>
<li><p>The next.ServeHTTP(w, r) call passes control to the next handler in the chain.</p>
</li>
<li><p>r.Use(LoggingMiddleware) applies the middleware to all routes defined in the router.</p>
</li>
</ul>
<p>When you run this code and visit http://localhost:8080, you’ll see a log like:</p>
<pre><code class="lang-text">2025/05/24 18:40:00 Request: GET /
</code></pre>
<h3 id="heading-step-3-using-chis-built-in-middleware">Step 3: Using Chi’s Built-in Middleware</h3>
<p>Chi comes with several built-in middleware functions for common tasks. For example, you can use chi.Logger for request logging or chi.Recoverer to catch panics. Here’s how to use them:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5/middleware"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    r := chi.NewRouter()

    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    r.Get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Hello, World!"</span>))
    })

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<p>The middleware.Logger outputs detailed logs for each request, including status codes and response times.</p>
<h3 id="heading-step-4-applying-middleware-to-specific-routes">Step 4: Applying Middleware to Specific Routes</h3>
<p>You can apply middleware to specific routes or groups of routes using Chi’s Group or Route methods. For example, let’s apply an authentication middleware only to a protected route:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">AuthMiddleware</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        token := r.Header.Get(<span class="hljs-string">"Authorization"</span>)
        <span class="hljs-keyword">if</span> token != <span class="hljs-string">"valid-token"</span> {
            http.Error(w, <span class="hljs-string">"Unauthorized"</span>, http.StatusUnauthorized)
            <span class="hljs-keyword">return</span>
        }
        next.ServeHTTP(w, ProtectedHandler)
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProtectedHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Welcome to the protected route!"</span>))
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    r := chi.NewRouter()

    r.Get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Public route"</span>))
    })

    r.Group(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(r chi.Router)</span></span> {
        r.Use(AuthMiddleware)
        r.Get(<span class="hljs-string">"/protected"</span>, ProtectedHandler)
    })

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<p>In this example:</p>
<ul>
<li><p>The / route is public and doesn’t require authentication.</p>
</li>
<li><p>The /protected route requires a valid Authorization header.</p>
</li>
<li><p>The AuthMiddleware checks for a token and either allows or denies access.</p>
</li>
</ul>
<h3 id="heading-step-5-working-with-request-context">Step 5: Working with Request Context</h3>
<p>Middleware can also modify the request context to pass data to downstream handlers. Here’s an example that adds a user ID to the request context:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">UserMiddleware</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        ctx := context.WithValue(r.Context(), <span class="hljs-string">"userID"</span>, <span class="hljs-string">"12345"</span>)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    r := chi.NewRouter()

    r.Use(UserMiddleware)

    r.Get(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        userID := r.Context().Value(<span class="hljs-string">"userID"</span>).(<span class="hljs-keyword">string</span>)
        w.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"User ID: "</span> + userID))
    })

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<p>Here, UserMiddleware adds a userID to the request context, which the route handler retrieves and uses.</p>
<h2 id="heading-best-practices-for-middleware-in-chi">Best Practices for Middleware in Chi</h2>
<ol>
<li><p>Keep Middleware Focused: Each middleware should handle a single responsibility (e.g., logging, authentication).</p>
</li>
<li><p>Order Matters: Middleware is executed in the order it’s registered. Place critical middleware (e.g., authentication) before others.</p>
</li>
<li><p>Use Context Sparingly: Only store necessary data in the request context to avoid clutter.</p>
</li>
<li><p>Handle Errors Gracefully: Use middleware like middleware.Recoverer to prevent crashes from panics.</p>
</li>
<li><p>Test Middleware: Write unit tests to ensure middleware behaves as expected, especially for authentication or validation logic.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Middleware in Go with Chi is a clean and flexible way to handle cross-cutting concerns in your web application. By leveraging Chi’s middleware system, you can modularize your code, improve maintainability, and build robust APIs. Whether you’re using built-in middleware like Logger or writing custom middleware for authentication, Chi makes it easy to integrate into your routes.</p>
<p>Try experimenting with Chi’s middleware in your next Go project, and let us know in the comments how you’re using it! For more details, check out the <a target="_blank" href="https://github.com/go-chi/chi">Chi documentation</a>.</p>
]]></content:encoded></item></channel></rss>