<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Web Development on WebDevStation</title>
    <link>https://webdevstation.com/categories/web-development/</link>
    <description>9 articles in the Web Development category — tutorials, code examples and notes from building real systems, newest first.</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor>Alex</managingEditor>
    <webMaster>Alex</webMaster>
    <copyright>© 2026 WebDevStation</copyright>
    <lastBuildDate>Tue, 25 Aug 2026 09:00:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/categories/web-development/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Rate Limiting Go APIs with golang.org/x/time/rate</title>
      <link>https://webdevstation.com/posts/rate-limiting-go-apis/</link>
      <pubDate>Tue, 25 Aug 2026 09:00:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/rate-limiting-go-apis/</guid>
      <description>Protect your Go HTTP APIs with token bucket rate limiting: per-client limiters, middleware, the standard rate limit headers, client-side throttling and when to move…</description>
      <content:encoded><![CDATA[<p>Somebody eventually points a badly written script at your API. Not maliciously — usually it is a colleague&rsquo;s retry loop with no back-off, or a cron job that fires every minute and takes ninety seconds. Without a limit, one client can consume the capacity you were saving for everyone else. <code>golang.org/x/time/rate</code> handles this in about as much code as it takes to describe, and it is the piece I now add before the first public endpoint ships.</p>
<h2 id="the-token-bucket-in-one-paragraph">The Token Bucket, in One Paragraph</h2>
<p>Picture a bucket that holds <code>b</code> tokens and refills at <code>r</code> tokens per second. Every request takes one token. If the bucket is empty, the request is rejected (or waits). That is the whole model, and it has one property that makes it the right default: <code>b</code> is a <strong>burst</strong> allowance. A client that has been idle can spend its accumulated tokens all at once, then settles into the steady rate. Real traffic is bursty — a page load firing six API calls should not be punished, while a loop firing six hundred should.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/time/rate&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 10 requests per second, bursts of up to 20.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">limiter</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">20</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Allow</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// over budget</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three methods, for three different situations:</p>
<table>
	<thead>
			<tr>
					<th>Method</th>
					<th>Behaviour</th>
					<th>Use it for</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Allow()</code></td>
					<td>Returns immediately: true or false</td>
					<td>Inbound HTTP — reject with 429</td>
			</tr>
			<tr>
					<td><code>Wait(ctx)</code></td>
					<td>Blocks until a token is free or ctx ends</td>
					<td>Outbound calls you control</td>
			</tr>
			<tr>
					<td><code>Reserve()</code></td>
					<td>Reserves a token, tells you the delay</td>
					<td>When you need to report <code>Retry-After</code></td>
			</tr>
	</tbody>
</table>
<p><code>rate.Limit</code> is a float, so fractional rates work: <code>rate.Every(time.Minute/100)</code> is 100 per minute, and <code>rate.Limit(0.5)</code> is one request every two seconds. <code>rate.Inf</code> disables limiting entirely, which is handy for a per-plan configuration where some tier is unlimited.</p>
<h2 id="a-global-limiter-is-not-enough">A Global Limiter Is Not Enough</h2>
<p>The naive version puts one limiter in front of everything:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">global</span> = <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">100</span>, <span style="color:#ae81ff">200</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">limit</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">global</span>.<span style="color:#a6e22e">Allow</span>() {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;too many requests&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This protects your <em>server</em> but not your <em>users</em>: one aggressive client can still eat the entire global budget and everyone else gets 429s. A global limiter is a useful backstop, not a fairness mechanism. What you want is a limiter per client, with the global one behind it.</p>
<h2 id="per-client-limiters">Per-Client Limiters</h2>
<p>Keep a map from client key to limiter, guarded by a mutex, with a janitor that evicts idle entries so the map does not grow forever.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">ratelimit</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;golang.org/x/time/rate&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">client</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">limiter</span>  <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">lastSeen</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Store hands out one limiter per key and forgets keys that go quiet.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Store</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">mu</span>      <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">Mutex</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">clients</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">client</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rate</span>    <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">burst</span>   <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ttl</span>     <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">r</span> <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>, <span style="color:#a6e22e">burst</span> <span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">ttl</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Store</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">clients</span>: make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">client</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">rate</span>:    <span style="color:#a6e22e">r</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">burst</span>:   <span style="color:#a6e22e">burst</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ttl</span>:     <span style="color:#a6e22e">ttl</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">cleanup</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">s</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Limiter returns the limiter for key, creating it on first use.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">Limiter</span>(<span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">c</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>[<span style="color:#a6e22e">key</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">c</span> = <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">client</span>{<span style="color:#a6e22e">limiter</span>: <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">rate</span>, <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">burst</span>)}
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>[<span style="color:#a6e22e">key</span>] = <span style="color:#a6e22e">c</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">lastSeen</span> = <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">limiter</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">cleanup</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ticker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTicker</span>(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ttl</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">C</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">lastSeen</span>) &gt; <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ttl</span> {
</span></span><span style="display:flex;"><span>                delete(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The mutex is not optional. A bare <code>map[string]*rate.Limiter</code> written from concurrent handlers is a textbook data race, and Go&rsquo;s runtime will happily crash the process with <code>concurrent map writes</code> — the same failure I dug into in <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a>.</p>
<p>Two design notes. <code>sync.Map</code> is not a better fit here: it is optimised for read-mostly workloads with stable keys, and this map is written on every new client. And an unbounded map is a memory-exhaustion vector if the key is attacker-controlled — hence the TTL. For a hard cap, put an LRU in front of it.</p>
<h2 id="the-middleware">The Middleware</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">limiter</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">Limiter</span>(<span style="color:#a6e22e">clientKey</span>(<span style="color:#a6e22e">r</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Reserve, rather than Allow, so we can report Retry-After.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">res</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Reserve</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">OK</span>() {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Burst is smaller than the request size; never satisfiable.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;rate limit misconfigured&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">delay</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">Delay</span>(); <span style="color:#a6e22e">delay</span> &gt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// We are not going to wait, so give the token back.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">Cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;Retry-After&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(int(<span style="color:#a6e22e">math</span>.<span style="color:#a6e22e">Ceil</span>(<span style="color:#a6e22e">delay</span>.<span style="color:#a6e22e">Seconds</span>()))))
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Limit&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Burst</span>()))
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Remaining&#34;</span>, <span style="color:#e6db74">&#34;0&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Reset&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(int(<span style="color:#a6e22e">math</span>.<span style="color:#a6e22e">Ceil</span>(<span style="color:#a6e22e">delay</span>.<span style="color:#a6e22e">Seconds</span>()))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">NewEncoder</span>(<span style="color:#a6e22e">w</span>).<span style="color:#a6e22e">Encode</span>(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#34;error&#34;</span>: <span style="color:#e6db74">&#34;rate limit exceeded&#34;</span>,
</span></span><span style="display:flex;"><span>                })
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>res.Cancel()</code> is the line people forget. <code>Reserve</code> takes the token immediately; if you then decide not to wait, cancelling returns it to the bucket. Skip it and every rejected request still consumes budget, so a client that trips the limit stays locked out far longer than intended.</p>
<p>This plugs into any router the same way as the handlers in my <a href="/posts/go-middleware-example/">Go middleware example</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">store</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>(<span style="color:#ae81ff">10</span>), <span style="color:#ae81ff">20</span>, <span style="color:#ae81ff">10</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span>))
</span></span></code></pre></div><h2 id="choosing-the-client-key">Choosing the Client Key</h2>
<p>This is where rate limiting is usually got wrong, and it is worth more thought than the algorithm.</p>
<p><strong>Authenticated requests: key on the identity.</strong> An API key or user ID is stable, meaningful, and cannot be spoofed once you have verified the token. If you are issuing JWTs — as in <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a> — the subject claim is your key.</p>
<p><strong>Anonymous requests: key on the IP, carefully.</strong> <code>r.RemoteAddr</code> behind a proxy is the proxy&rsquo;s address, so every user shares one bucket. But blindly trusting <code>X-Forwarded-For</code> is worse: it is a client-supplied header, and anyone can put whatever they like in it to get a fresh bucket per request.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">clientKey</span>(<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Authenticated callers are keyed on identity.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">userID</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">UserFrom</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>()); <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;user:&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">userID</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Only trust the proxy header if the request came from our proxy,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// and take the address the proxy appended — the rightmost hop.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ip</span>, <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">SplitHostPort</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">RemoteAddr</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">isTrustedProxy</span>(<span style="color:#a6e22e">ip</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">xff</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Header</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;X-Forwarded-For&#34;</span>); <span style="color:#a6e22e">xff</span> <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">parts</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">xff</span>, <span style="color:#e6db74">&#34;,&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">ip</span> = <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">TrimSpace</span>(<span style="color:#a6e22e">parts</span>[len(<span style="color:#a6e22e">parts</span>)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>])
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;ip:&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">ip</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The rightmost entry is the one your own proxy added; everything to its left came from the client and is unverifiable. If your platform provides a trusted header — Cloudflare&rsquo;s <code>CF-Connecting-IP</code>, or the standard <code>Forwarded</code> from a proxy you control — prefer it.</p>
<p>One more refinement: not all endpoints are equal. <code>POST /reports/export</code> might cost a hundred times what <code>GET /health</code> does. <code>AllowN</code> and <code>ReserveN</code> let you charge by cost:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">cost</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Method</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodPost</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">HasPrefix</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>, <span style="color:#e6db74">&#34;/reports&#34;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cost</span> = <span style="color:#ae81ff">25</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">res</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">ReserveN</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>(), <span style="color:#a6e22e">cost</span>)
</span></span></code></pre></div><p>Just keep the burst at least as large as your most expensive operation, or <code>res.OK()</code> returns false forever and that endpoint becomes permanently unreachable.</p>
<h2 id="limiting-yourself-too">Limiting Yourself, Too</h2>
<p>Rate limiting is not only defensive. When you are the client of somebody else&rsquo;s API, respecting their limit proactively beats absorbing 429s and retrying. <code>Wait</code> is built for this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Client</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>    <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">limiter</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewClient</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">Client</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Client</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http</span>:    <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>{<span style="color:#a6e22e">Timeout</span>: <span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>},
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// The upstream allows 5 requests/second; stay under it.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">limiter</span>: <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">5</span>, <span style="color:#ae81ff">5</span>),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Client</span>) <span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">req</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Response</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Blocks until a token is available, or ctx is cancelled.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Wait</span>(<span style="color:#a6e22e">ctx</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;rate limiter: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>Wait</code> returns an error if the context is cancelled or if its deadline arrives before a token would — so a caller that has already given up never sits in the queue. That is the <a href="/posts/understanding-golang-context/">context</a> machinery doing exactly what it is for.</p>
<p>This composes neatly with a bounded worker pool: the pool caps how many requests are <em>in flight</em>, the limiter caps how many <em>start per second</em>. They constrain different things and you usually want both, as I covered in <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>.</p>
<h2 id="where-this-approach-stops-working">Where This Approach Stops Working</h2>
<p>Be honest about the limits of an in-process limiter.</p>
<p><strong>It is per instance.</strong> Three replicas with a limit of 10/s allow 30/s in total, and a client bouncing between them gets a fresh bucket each time. For a real global limit you need shared state — Redis with a Lua script that does the token accounting atomically, or a limiter at the edge.</p>
<p><strong>It is lost on restart.</strong> Every deploy resets every bucket. Usually fine; occasionally not.</p>
<p><strong>It costs you a request.</strong> The request still reaches your process, gets routed, and allocates before being rejected. Under a genuine flood, that is exactly the work you cannot afford — which is why volumetric protection belongs at the CDN or load balancer, not in your handler.</p>
<p>My rule of thumb: <strong>application limits enforce fairness and per-plan quotas; edge limits absorb abuse.</strong> They solve different problems and you want both. The nginx layer is a natural place for the coarse one, and it can be surprisingly nuanced — the <a href="/posts/how-to-make-nginx-cookie-aware/">cookie-aware caching</a> tricks work the same way for keying <code>limit_req</code> zones.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">limit_req_zone</span> $binary_remote_addr <span style="color:#e6db74">zone=api:10m</span> <span style="color:#e6db74">rate=100r/s</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">location</span> <span style="color:#e6db74">/api/</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">limit_req</span> <span style="color:#e6db74">zone=api</span> <span style="color:#e6db74">burst=200</span> <span style="color:#e6db74">nodelay</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://backend</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="testing-it">Testing It</h2>
<p>Rate limiting is easy to test badly, because <code>time.Now()</code> is involved. Keep the rates small and explicit rather than sleeping through real seconds:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestLimiterRejectsBurst</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">store</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>(<span style="color:#ae81ff">1</span>), <span style="color:#ae81ff">3</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">h</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span>)(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) { <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>) },
</span></span><span style="display:flex;"><span>    ))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">codes</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">codes</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">req</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">httptest</span>.<span style="color:#a6e22e">NewRequest</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodGet</span>, <span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">RemoteAddr</span> = <span style="color:#e6db74">&#34;203.0.113.7:1234&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">rec</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">httptest</span>.<span style="color:#a6e22e">NewRecorder</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">rec</span>, <span style="color:#a6e22e">req</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">codes</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">rec</span>.<span style="color:#a6e22e">Code</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Burst of 3 succeeds, the rest are rejected.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">want</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">int</span>{<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">429</span>, <span style="color:#ae81ff">429</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">slices</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">codes</span>, <span style="color:#a6e22e">want</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;got %v, want %v&#34;</span>, <span style="color:#a6e22e">codes</span>, <span style="color:#a6e22e">want</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then confirm the behaviour under real load before you trust the number. Pointing a load test at the endpoint and watching the ratio of 200s to 429s tells you whether your limit matches the traffic you actually get — the setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> is enough for this.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>Per-client limiters, not one global bucket, with a global one as backstop.</li>
<li>Key on identity when authenticated; on a <em>verified</em> IP otherwise.</li>
<li>Evict idle limiters so the map cannot grow without bound.</li>
<li><code>res.Cancel()</code> whenever you reject instead of waiting.</li>
<li>Send <code>Retry-After</code> and <code>RateLimit-*</code> headers so good clients can behave.</li>
<li>Burst at least as large as your most expensive weighted operation.</li>
<li><code>Wait(ctx)</code> on the client side of other people&rsquo;s APIs.</li>
<li>Volumetric protection at the edge, fairness in the application.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p><code>golang.org/x/time/rate</code> is one of those packages that does exactly one thing and does it without ceremony. The algorithm is not the hard part — picking a sensible client key, giving tokens back when you reject, and being clear about what an in-process limiter can and cannot promise is where the real work is. Get those right and a single misbehaving script stops being everybody else&rsquo;s problem. And if the API you are the client of happens to be a model provider, an agent loop is a remarkably efficient way to find its limits — see <a href="/posts/tool-use-in-go-agent-loop/">tool use in Go</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Backend Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>An Easy Way to Generate QR Codes Fast</title>
      <link>https://webdevstation.com/posts/aneasywaytogenerateqrcodefast/</link>
      <pubDate>Thu, 08 May 2025 17:00:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/aneasywaytogenerateqrcodefast/</guid>
      <description>Discover the most efficient ways to create QR codes for your projects with these powerful tools and techniques that every developer should know.</description>
      <content:encoded><![CDATA[<p>Every few months a project of mine needs a QR code — a link on a conference badge, Wi-Fi credentials for an office, a deep link into a mobile app. Each time I rediscover that generating one is far easier than it looks, provided you know which tool to reach for.</p>
<h2 id="the-surprising-power-of-qr-codes-in-modern-development">The Surprising Power of QR Codes in Modern Development</h2>
<p>Remember when QR codes seemed like a passing tech fad? Fast forward to today, and these pixelated squares have revolutionized how we connect the physical and digital worlds. As developers, we&rsquo;re constantly looking for frictionless ways to bridge this gap, and QR codes offer an elegant solution hiding in plain sight.</p>
<p>I&rsquo;ve spent considerable time exploring various QR code generation methods for both client projects and personal use. In this post, I&rsquo;ll share the most efficient approaches I&rsquo;ve discovered, helping you implement QR functionality without unnecessary complexity.</p>
<h2 id="why-qr-codes-are-a-developers-secret-weapon">Why QR Codes Are a Developer&rsquo;s Secret Weapon</h2>
<p>Before we dive into implementation specifics, let&rsquo;s acknowledge what makes QR codes particularly valuable in our development toolkit:</p>
<ul>
<li><strong>Friction reduction</strong> — Eliminate tedious URL typing with a simple scan</li>
<li><strong>Protocol versatility</strong> — Handle everything from basic URLs to complex Wi-Fi configurations</li>
<li><strong>Error correction</strong> — Built-in redundancy ensures functionality even with partial damage</li>
<li><strong>Adaptive data density</strong> — Automatically optimize the pattern based on content length</li>
<li><strong>Offline functionality</strong> — No internet required for the scanning process itself</li>
</ul>
<h2 id="streamlined-qr-generation-methods">Streamlined QR Generation Methods</h2>
<h3 id="chrome-devtools-the-hidden-feature-youre-missing">Chrome DevTools: The Hidden Feature You&rsquo;re Missing</h3>
<p>If you need a quick QR code during development, Chrome has you covered with a built-in generator that many developers overlook.</p>
<ol>
<li>Open DevTools (F12 or Cmd+Opt+I)</li>
<li>Click the &ldquo;three dots&rdquo; menu</li>
<li>Navigate to More tools → Network conditions</li>
<li>Find the QR code icon in the toolbar</li>
</ol>
<p>This approach is perfect for quickly sharing your localhost development server with mobile devices for testing.</p>
<h3 id="power-user-libraries-for-programmatic-generation">Power-User Libraries for Programmatic Generation</h3>
<p>When building QR functionality into your applications, these libraries offer the best balance of performance and flexibility:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// Using qrcode.js - one of my favorite lightweight options
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">QRCode</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Generate QR code to a canvas element
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">QRCode</span>.<span style="color:#a6e22e">toCanvas</span>(document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">&#39;canvas&#39;</span>), <span style="color:#e6db74">&#39;https://webdevstation.com&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">errorCorrectionLevel</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;H&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">margin</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">scale</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">8</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">dark</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#000000&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">light</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#ffffff&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">error</span>(<span style="color:#a6e22e">error</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;QR code generated!&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>For backend implementations, Go has some excellent packages. I often use this approach in my Go projects:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Using go-qrcode for server-side QR generation</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;image/png&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;os&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/boombuler/barcode&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/boombuler/barcode/qr&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create the QR code</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">qrCode</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">Encode</span>(<span style="color:#e6db74">&#34;https://webdevstation.com&#34;</span>, <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">M</span>, <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">Auto</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Scale the QR code to 256x256 pixels</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">qrCode</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">barcode</span>.<span style="color:#a6e22e">Scale</span>(<span style="color:#a6e22e">qrCode</span>, <span style="color:#ae81ff">256</span>, <span style="color:#ae81ff">256</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a file to save the QR code</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Create</span>(<span style="color:#e6db74">&#34;webdevstation-qr.png&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">file</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Save the QR code as PNG</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">png</span>.<span style="color:#a6e22e">Encode</span>(<span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">qrCode</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;QR code generated successfully&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="the-hidden-gem-qrcodereact-for-react-applications">The Hidden Gem: QRCode.react for React Applications</h3>
<p>For React developers, I&rsquo;ve been particularly impressed with the <code>qrcode.react</code> package, which offers seamless integration with minimal overhead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-jsx" data-lang="jsx"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">React</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;react&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">QRCodeSVG</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode.react&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">MyQRCode</span> <span style="color:#f92672">=</span> ({ <span style="color:#a6e22e">url</span> }) =&gt; (
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">QRCodeSVG</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">value</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">url</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">size</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">256</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">bgColor</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;#ffffff&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fgColor</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;#000000&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">level</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;H&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">includeMargin</span><span style="color:#f92672">=</span>{<span style="color:#66d9ef">false</span>}
</span></span><span style="display:flex;"><span>  /&gt;
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">MyQRCode</span>;
</span></span></code></pre></div><p>The SVG output ensures sharp rendering at any size while keeping the bundle size minimal.</p>
<h2 id="advanced-techniques-for-professional-implementation">Advanced Techniques for Professional Implementation</h2>
<h3 id="dynamic-qr-codes-the-game-changer">Dynamic QR Codes: The Game Changer</h3>
<p>Statically generated QR codes work well for permanent links, but for marketing campaigns or situations where the destination might change, dynamic QR codes offer a crucial advantage.</p>
<p>I recently built a solution using Firebase Dynamic Links combined with custom QR generation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">getDynamicLink</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;firebase/dynamic-links&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">QRCode</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">generateDynamicQRCode</span>(<span style="color:#a6e22e">destinationUrl</span>, <span style="color:#a6e22e">campaignId</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Create a short dynamic link first
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">dynamicLink</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">getDynamicLink</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">longDynamicLink</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`https://myapp.page.link/?link=</span><span style="color:#e6db74">${</span>encodeURIComponent(<span style="color:#a6e22e">destinationUrl</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">&amp;apn=com.myapp&amp;afl=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">campaignId</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">suffix</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">option</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;SHORT&#39;</span> }
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Then generate QR code with the dynamic link
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">qrCodeDataUrl</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">QRCode</span>.<span style="color:#a6e22e">toDataURL</span>(<span style="color:#a6e22e">dynamicLink</span>.<span style="color:#a6e22e">shortLink</span>, {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">errorCorrectionLevel</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;H&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">dark</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#3B82F6&#39;</span>, <span style="color:#75715e">// Blue
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">light</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#ffffff&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">qrCodeDataUrl</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This approach allows you to change the destination URL without regenerating the QR code itself – invaluable for printed materials or permanent displays.</p>
<h3 id="design-techniques-that-increase-scan-rates">Design Techniques That Increase Scan Rates</h3>
<p>A common misconception is that QR codes must remain strictly black and white. In reality, QR codes can maintain functionality with significant customization when implemented correctly.</p>
<p>The key is understanding the error correction levels:</p>
<ul>
<li><strong>Level L:</strong> 7% error correction</li>
<li><strong>Level M:</strong> 15% error correction</li>
<li><strong>Level Q:</strong> 25% error correction</li>
<li><strong>Level H:</strong> 30% error correction</li>
</ul>
<p>With Level H, you can integrate logos, use custom colors, and even apply moderate styling effects while maintaining reliable functionality.</p>
<h2 id="best-practices-from-real-world-implementation">Best Practices from Real-World Implementation</h2>
<p>Through trial and error across numerous projects, I&rsquo;ve learned that successful QR code implementation comes down to these critical factors:</p>
<ol>
<li>
<p><strong>Test extensively</strong> — Always verify your QR codes on multiple devices and in varying lighting conditions</p>
</li>
<li>
<p><strong>Prioritize contrast</strong> — While custom colors are possible, maintaining high contrast between the foreground and background is essential</p>
</li>
<li>
<p><strong>Size appropriately</strong> — The minimum recommended size is 2cm × 2cm for reliable scanning, but always err toward larger when possible</p>
</li>
<li>
<p><strong>Add clear instructions</strong> — A simple &ldquo;Scan me&rdquo; prompt significantly increases user engagement</p>
</li>
<li>
<p><strong>Include fallback options</strong> — Always provide an alternative access method for users who may have difficulty scanning</p>
</li>
</ol>
<h2 id="the-user-friendly-alternative">The User-Friendly Alternative</h2>
<p>While libraries provide great flexibility for developers, sometimes you need a quick solution without writing code. During my research, I discovered <a href="https://qrcodia.com/">QRcodia</a> – a tool that embodies the clean, minimalist approach I value in web services.</p>
<p>Unlike most free generators that bombard you with ads or hide essential features behind paywalls, <a href="https://qrcodia.com/">QRcodia</a> offers a streamlined experience with useful features completely free:</p>
<ul>
<li><strong>Multiple QR code types</strong>: Create codes for URLs, text, WiFi credentials, contact information (vCard), and even calendar events</li>
<li><strong>Customizable design</strong>: Adjust colors, add logos, and change shapes to match your brand</li>
<li><strong>High-quality downloads</strong>: Export as scalable SVG or high-resolution PNG</li>
<li><strong>No account required</strong>: Generate and download immediately without registration</li>
</ul>
<p>For one of my recent projects where team members needed to frequently create QR codes but weren&rsquo;t developers, I recommended this tool. The ability to customize the visual appearance while maintaining scannability made it particularly valuable for creating branded marketing materials.</p>
<h2 id="conclusion-simplicity-wins">Conclusion: Simplicity Wins</h2>
<p>After exploring dozens of QR code generation methods, I&rsquo;ve found that the most effective approach is nearly always the simplest one that meets your requirements. While feature-rich QR services exist, they often add unnecessary complexity.</p>
<p>My go-to solution remains a lightweight library like qrcode.js for frontend applications or the boombuler/barcode package for Go backend systems. For more complex needs requiring analytics or dynamic destinations, a specialized service can be worth considering.</p>
<p>The beauty of QR technology lies in its accessibility and simplicity – qualities we should preserve in our implementations.</p>
<p>What&rsquo;s your experience with QR code implementation? Have you discovered any clever techniques or libraries that have simplified your development process? I&rsquo;d love to hear about your approaches in the comments below.</p>
<p>If you build a small generator of your own, <a href="/posts/one-of-thee-easiest-ways-to-host-go-web-apps/">one of the easiest ways to host your Go web app</a> covers getting it online for about five dollars a month. And for another tool that quietly improved my week, see <a href="/posts/enhancing-reading-experience-with-music-and-booktuning/">enhancing your reading experience with music and BookTuning</a>.</p>]]></content:encoded>
      <category>Web Development</category>
      <category>Tools</category>
    </item>
    <item>
      <title>One of The Easiest Ways to Host your Go Web App</title>
      <link>https://webdevstation.com/posts/one-of-thee-easiest-ways-to-host-go-web-apps/</link>
      <pubDate>Tue, 05 Sep 2023 09:53:15 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/one-of-thee-easiest-ways-to-host-go-web-apps/</guid>
      <description>Discover how to host Go web applications for as little as $5 per month using DigitalOcean and Docker. Learn this cost-effective, scalable deployment method with…</description>
      <content:encoded><![CDATA[<p>Hello! In this post, I will explain the cost-effective method I use to host my Go web applications
with varying levels of complexity, all starting from as low as $5 per month. This method also allows to easy
deploy and scale your golang application.</p>
<p>As an example, this is how I host <a href="https://whattoreadafter.xyz" title="What to read after: AI book recommendations">whattoreadafter.xyz</a>, a service that recommends books based on the book you just finished reading, by using AI.</p>
<p>Starting off, let&rsquo;s enumerate the tools we&rsquo;ll be using alongside Golang:</p>
<ul>
<li><a href="https://m.do.co/c/2a29ebc23e4a">DigitalOcean</a> - a cloud computing platform that provides virtual machines and other resources.</li>
<li><a href="https://www.docker.com/">Docker</a> - a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers.</li>
</ul>
<p>In order to follow along, you will need to have a DigitalOcean account. If you don&rsquo;t have one, you can
sign up <a href="https://m.do.co/c/2a29ebc23e4a">here</a> and get $200 in credit over 60 days.</p>
<p>Let&rsquo;s get started!
For simplicity sake, our application will be a simple web server that returns current time in UTC format.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">currentTimeHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">currentTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">UTC</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprintf</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;Current Time (UTC): %s&#34;</span>, <span style="color:#a6e22e">currentTime</span>.<span style="color:#a6e22e">Format</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">RFC3339</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">currentTimeHandler</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Server is running on port 8080&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:8080&#34;</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now let&rsquo;s host it!</p>
<p>In order for you to easier follow along, I will list the steps we will take to host our application:</p>
<h2 id="1-prepare-a-dockerfile-for-our-application-and-place-it-in-the-root-of-our-project-alongside-the-maingo-file">1. Prepare a Dockerfile for our application and place it in the root of our project alongside the <code>main.go</code> file.</h2>
<p>Example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">golang:alpine</span> <span style="color:#66d9ef">AS</span> <span style="color:#e6db74">builder</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> apk add --no-cache --update <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        git <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        ca-certificates<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">ADD</span> . /app<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">WORKDIR</span> <span style="color:#e6db74">/app</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">COPY</span> go.mod ./<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span>  go mod download<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> CGO_ENABLED<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go build -a -o /main .<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">alpine</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">COPY</span> --from<span style="color:#f92672">=</span>builder /main ./<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> chmod +x ./main<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">ENTRYPOINT</span> [<span style="color:#e6db74">&#34;./main&#34;</span>]<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p> </p>
<h2 id="2-push-your-code-to-a-github-repository-you-can-make-it-private-if-you-want">2. Push your code to a GitHub repository. You can make it private if you want.</h2>
<p> </p>
<h2 id="3-go-to-digitalocean-and-create-a-new-app-you-can-do-so-by-clicking-on-the-apps-tab-in-the-left-sidebar-and-then-clicking-create-app">3. Go to <a href="https://cloud.digitalocean.com/apps">DigitalOcean</a> and create a new App. You can do so by clicking on the &ldquo;Apps&rdquo; tab in the left sidebar and then clicking &ldquo;Create App&rdquo;.</h2>
<p> </p>
<h2 id="4-select-github-as-your-source-and-click-continue">4. Select &ldquo;GitHub&rdquo; as your &ldquo;Source&rdquo; and click &ldquo;Continue&rdquo;.</h2>
<p><img src="/images/2023/do1.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="5-select-the-repository-you-want-to-deploy-and-click-next">5. Select the repository you want to deploy and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do2.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="6-select-dockerfile-as-your-build-type-and-click-next">6. Select &ldquo;Dockerfile&rdquo; as your &ldquo;Build type&rdquo; and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do3.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="7-configure-environment-variables-if-you-need-to-and-click-next">7. Configure environment variables if you need to and click &ldquo;Next&rdquo;.</h2>
<p> </p>
<h2 id="8-select-region-for-your-app-and-click-next">8. Select region for your app and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do4.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="9-in-the-billing-section-select-the-plan-you-want-to-use-you-can-start-with-the-5-per-month-plan-and-scale-up-later-if-you-need-to">9. In the billing section, select the plan you want to use. You can start with the $5 per month plan and scale up later if you need to.</h2>
<p><img src="/images/2023/do5.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="10-click-create-resources-and-wait-for-your-app-to-be-deployed">10. Click &ldquo;Create Resources&rdquo; and wait for your app to be deployed.</h2>
<p> </p>
<h2 id="11-once-your-app-is-deployed-you-can-access-it-by-clicking-on-the-live-app-link">11. Once your app is deployed, you can access it by clicking on the &ldquo;Live App&rdquo; link.</h2>
<p><img src="/images/2023/do6.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<p>That&rsquo;s it! You have successfully deployed your Golang application!</p>
<p>You can also add your own domain name to your app by clicking on the &ldquo;Settings&rdquo; tab and then clicking on &ldquo;Domains&rdquo;.</p>
<p>Now, every time you push a new commit to your repository, DigitalOcean will automatically build and deploy your application.
This is a great way to host your Golang applications, especially if you are just starting out and don&rsquo;t want to spend a lot of money on hosting.</p>
<p>Thank you for reading! If you have any questions, feel free to reach out to me on <a href="https://twitter.com/oleks_i">Twitter</a>.</p>
<p>Before you point real traffic at it, two things are worth having in place: <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown</a>, so redeploys stop dropping requests, and <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">a load test</a>, so you know what the box can actually take.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>DevOps</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>103 Early Hints in Go, or the new Way of How to Improve Performance of a Web Page written in Go</title>
      <link>https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</link>
      <pubDate>Mon, 14 Nov 2022 19:40:21 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</guid>
      <description>Learn how to implement HTTP 103 Early Hints in Go 1.19+ to significantly improve web page loading performance by enabling browsers to preload resources while waiting…</description>
      <content:encoded><![CDATA[<p>Since Go 1.19 we can use a new <code>103 (Early Hints)</code> http status code when we create web applications. Let&rsquo;s figure out how and when this could help us.
We are going to create a simple golang web server that servers some html content. One html page will be served with <code>103</code> header and another one without.
After loading comparison we will see how early hints can improve page performance.</p>
<p>Early hints is a special HTTP header that is sent before the web server sends the final HTTP response to the client. At this moment it&rsquo;s supported only by Chrome browser.
As soon as the browser requests a page, server immediately returns 103 early hints header. In the meantime, a server will generate a usual HTTP response. This helps us utilize in maximum the loading time by letting browser know what resources it should preload while waiting for the final response from a server.</p>
<p>Enough theory, let&rsquo;s write some code :)</p>
<p>First, I&rsquo;m going to create an index.html with some dummy structure. Also, I will load <code>bootsrap</code> frontend framework to simulate some heavy css and js references during page load.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">title</span>&gt;Hello!&lt;/<span style="color:#f92672">title</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">link</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&#34;</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stylesheet&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;p-2 bg-success&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-white&#34;</span>&gt;Hello!&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Now we need to serve it. Let&rsquo;s create a server.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//go:embed index.html</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">index</span> <span style="color:#66d9ef">string</span> <span style="color:#75715e">// embeded index.html</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Starting server...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page without early hints.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/1&#34;</span>, <span style="color:#a6e22e">noHintsHandler</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page with early hints</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/2&#34;</span>, <span style="color:#a6e22e">withHintsHandler</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:8082&#34;</span>, <span style="color:#66d9ef">nil</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">noHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">withHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Adding headers with preload information for bootstrap.min.css and bootstrap.bundle.min.js</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&gt;; rel=preload; as=style&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&gt;; rel=preload; as=script&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Sending 103 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusEarlyHints</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Sending 200 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now it&rsquo;s time to see our pages in action! Run our server <code>go run main.go</code>, open the page without early hints <code>http://localhost:8082/1</code> in Chrome,
open inspector, go to Lighthouse tab and click on &ldquo;Analyze page load&rdquo; button.
And this is what we can see:
<img src="/images/2022/1.png" alt="Chrome Lighthouse report for the Go page without early hints, showing a First Contentful Paint of 1492.8ms" title="Performance results for the page without early hints">
It takes a while until bootstrap resources got loaded by a browser. As result, FCP (First Contentful Paint) is <code>1492,8ms</code>.</p>
<p>Now, let&rsquo;s do the same for the page with the early hints <code>http://localhost:8082/2</code> And this is a result:
<img src="/images/2022/2.png" alt="Chrome Lighthouse report for the same page served with 103 Early Hints, showing a First Contentful Paint of 437.8ms" title="Performance results for the page with early hints">
As you can see, the page loaded much faster now. Bootstrap dependencies (bootstrap.min.css and bootstrap.bundle.min.js) were preloaded in the beginning and FCP now is <code>437,8ms</code>. More than 3 times faster, quite an impressive result!</p>
<p>However, it does not mean that you have to preload absolutely all resources now. Just try to experiment with these things, see how it affects your page performance and decide for yourself the right balance.</p>
<p>You can find the source code <a href="https://github.com/alexsergivan/blog-examples/tree/master/early-hints">here</a>.</p>
<p>If you want to measure the difference on your own service rather than take my numbers for it, <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> shows the setup I use. And for the wins that happen before the request even reaches your handler, have a look at <a href="/posts/how-to-make-nginx-cookie-aware/">how to make Nginx cache cookie aware</a>.</p>]]></content:encoded>
      <category>Performance Optimization</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>How to Show Flash Messages in Go web applications (with Echo framework)</title>
      <link>https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/</link>
      <pubDate>Thu, 04 Feb 2021 17:28:00 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/</guid>
      <description>Learn how to implement flash messages in Go web applications using Echo framework and Gorilla Sessions to improve user experience by providing feedback after form…</description>
      <content:encoded><![CDATA[<p>When we create a web application, usually, there a need to communicate with the users to inform them about the results
of their actions. The easiest way to communicate - is to send messages. These messages might be warnings, errors, or just
informational text. In this article, we will improve the UX of our user authentication application from the <a href="https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/">previous article</a>
by adding an error flash message when the user entered a wrong password and a success message after user authorisation.</p>
<p>We are going to use cookies to store messages in-between requests. To not reinvent the wheel, we will install <a href="https://github.com/gorilla/sessions">Gorilla Sessions</a>
package.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">com</span><span style="color:#f92672">/</span><span style="color:#a6e22e">gorilla</span><span style="color:#f92672">/</span><span style="color:#a6e22e">sessions</span>
</span></span></code></pre></div><p>Next, let&rsquo;s create a <code>messages</code> package (messages/messages.go):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">messages</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/gorilla/sessions&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Name of the cookie.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">sessionName</span> = <span style="color:#e6db74">&#34;fmessages&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">getCookieStore</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">sessions</span>.<span style="color:#a6e22e">CookieStore</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// In real-world applications, use env variables to store the session key.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sessionKey</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;test-session-key&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">sessions</span>.<span style="color:#a6e22e">NewCookieStore</span>([]byte(<span style="color:#a6e22e">sessionKey</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Set adds a new message into the cookie storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">value</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getCookieStore</span>().<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">sessionName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">AddFlash</span>(<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Save</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>())
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Get gets flash messages from the cookie storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>) []<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getCookieStore</span>().<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">sessionName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fm</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Flashes</span>(<span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// If we have some messages.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">fm</span>) &gt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Save</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>())
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Initiate a strings slice to return messages.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">flashes</span> []<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">fl</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">fm</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// Add message to the slice.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">flashes</span> = append(<span style="color:#a6e22e">flashes</span>, <span style="color:#a6e22e">fl</span>.(<span style="color:#66d9ef">string</span>))
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">flashes</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We now have a possibility to easily Set and Get flash messages. Let&rsquo;s integrate it, first, to the sign-in form, which we created in the <a href="https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/">previous article</a>.</p>
<p>I&rsquo;m going to modify <code>controllers/signin.go</code> file. Inside <code>SignIn()</code> function we can replace</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Password is incorrect&#34;</span>)
</span></span></code></pre></div><p>with</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>, <span style="color:#e6db74">&#34;Password is incorrect!&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span></code></pre></div><p>And, if the password is correct, we going to set a message also:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;message&#34;</span>, <span style="color:#e6db74">&#34;Password is correct, you have been authenticated!&#34;</span>)
</span></span></code></pre></div><p>The final <code>SignIn()</code> function will look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// SignIn will be executed after SignInForm submission.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignIn</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Load our &#34;test&#34; user.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">storedUser</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">LoadTestUser</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Initiate a new User struct.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Parse the submitted data and fill the User struct with the data from the SignIn form.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Bind</span>(<span style="color:#a6e22e">u</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Compare the stored hashed password, with the hashed version of the password that was received</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">CompareHashAndPassword</span>([]byte(<span style="color:#a6e22e">storedUser</span>.<span style="color:#a6e22e">Password</span>), []byte(<span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Password</span>)); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// If the two passwords don&#39;t match, set a message and reload the page.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>, <span style="color:#e6db74">&#34;Password is incorrect!&#34;</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// If password is correct, generate tokens and set cookies.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">storedUser</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Token is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;message&#34;</span>, <span style="color:#e6db74">&#34;Password is correct, you have been authenticated!&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Once we set messages, we need to display them.</p>
<p>First, I will add message displaying logic to the SignIn form. We need to modify <code>SignInForm()</code> function inside <code>controllers/signin.go</code>, by adding additional data with messages to the template execution function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>), <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;messages.html&#34;</span>))
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">data</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">interface</span>{})
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#34;errors&#34;</span>] = <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#a6e22e">data</span>);
</span></span></code></pre></div><p>The final <code>SignInForm()</code> function will look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// SignInForm responsible for signIn Form rendering.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignInForm</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>), <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;messages.html&#34;</span>))
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">data</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">interface</span>{})
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#34;errors&#34;</span>] = <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#a6e22e">data</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>As you probably noticed, we introduced <code>messages.html</code> to the <code>template.ParseFiles()</code> function. There we will control how to visualize messages. Let&rsquo;s add this template inside <code>templates</code> folder:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{ define &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>  {{ if index . &#34;errors&#34; }}
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;errors&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;background: darksalmon; padding: 10px;&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        {{ range index . &#34;errors&#34; }}
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">p</span>&gt;{{ . }}&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>        {{ end }}
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  {{ end }}
</span></span><span style="display:flex;"><span>  {{ if index . &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;messages&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;background: darkseagreen; padding: 10px;&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        {{ range index . &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">p</span>&gt;{{ . }}&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>        {{ end }}
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  {{ end }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{{ end }}
</span></span></code></pre></div><p>Next, we need to add the reference to the <code>messages</code> template inside <code>signIn.html</code> and <code>admin.html</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>{{ <span style="color:#a6e22e">template</span> <span style="color:#e6db74">&#34;messages&#34;</span> . }}
</span></span></code></pre></div><p>Now, we can try how does it work.</p>
<p>Let&rsquo;s run the server <code>go run main.go</code> and go to the <code>/user/signin</code> path. When we submit the form with the wrong password, we should see this:
<img src="/images/0221/error.png" alt="Error Message" title="Error Message"></p>
<p>When a password is correct, we will be redirected to the <code>/admin</code> path with the message:
<img src="/images/0221/success.png" alt="Success Message" title="Success Message"></p>
<p>In this example, I was using the Echo framework, but it will be super easy to modify the code for your specific use-case.</p>
<p>The complete source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/flashmessages">here</a>.</p>
<p>Flash messages usually appear right after a login or a form post, so the natural companion pieces are <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a> and, for the layer that wraps every handler, <a href="/posts/go-middleware-example/">the Go middleware example</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>User Authentication in Go Echo with JWT</title>
      <link>https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/</link>
      <pubDate>Thu, 28 Jan 2021 18:05:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/</guid>
      <description>Learn how to implement secure user authentication in Go using JWT (JSON Web Tokens) with the Echo framework. This step-by-step guide covers token creation,…</description>
      <content:encoded><![CDATA[<p>In this article, we will build a simple user authentication functionality using JWT (JSON Web Token).
In the examples, I&rsquo;m going to use a Go <a href="https://echo.labstack.com/">Echo</a> framework. This will allow us
to avoid writing some boilerplate code.</p>
<p>If you are not familiar with a JWT theory, please refer to <a href="https://jwt.io/introduction/">this resource</a>.</p>
<p>I believe that the easiest way to understand how to work with JWT authentication is by solving a real-world problem.
Let&rsquo;s say, that we have a website with an administration section, that should be accessible only by authenticated users, by providing some credentials. If authentication was successful, the user can access the administration section. If the user is inactive during a defined period of time, we should log him out from the system.</p>
<p>In the beginning, I&rsquo;m going to create a <code>main.go</code> file with initialization of the web server and some routers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/auth&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/controllers&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/labstack/echo/v4/middleware&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">e</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">New</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Defining of the admin router group.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">adminGroup</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Group</span>(<span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Router for &#34;/admin&#34; path.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">Admin</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Starting the server.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Logger</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Start</span>(<span style="color:#e6db74">&#34;:8777&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And controllers/admin.go:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Admin</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">String</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>, <span style="color:#e6db74">&#34;Hi, you have access!&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you run this code and go to <code>http://localhost:8777/admin</code>, you will access this page without any authentication.
Let&rsquo;s protect this path, by adding a JWT authentication.</p>
<p>First, what I&rsquo;m going to create - it&rsquo;s an <code>auth</code> package, where we will keep all JWT related logic.
Please refer to the code below with added explanatory comments:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">auth</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgrijalva/jwt-go&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">accessTokenCookieName</span>  = <span style="color:#e6db74">&#34;access-token&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Just for the demo purpose, I declared a secret here. In the real-world application, you might need to get it from the env variables.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtSecretKey</span> = <span style="color:#e6db74">&#34;some-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetJWTSecret</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">jwtSecretKey</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Create a struct that will be encoded to a JWT.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// We add jwt.StandardClaims as an embedded type, to provide fields like expiry time.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Claims</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Name</span>  <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;name&#34;`</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">StandardClaims</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">accessTokenCookieName</span>, <span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the expiration time of the token (1h).</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">expirationTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">expirationTime</span>, []byte(<span style="color:#a6e22e">GetJWTSecret</span>()))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Pay attention to this function. It holds the main JWT token generation logic.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">expirationTime</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">secret</span> []<span style="color:#66d9ef">byte</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Create the JWT claims, which includes the username and expiry time.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">claims</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Claims</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Name</span>:  <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">StandardClaims</span>: <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">StandardClaims</span>{
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// In JWT, the expiry time is expressed as unix milliseconds.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">ExpiresAt</span>: <span style="color:#a6e22e">expirationTime</span>.<span style="color:#a6e22e">Unix</span>(),
</span></span><span style="display:flex;"><span>		},
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the token with the HS256 algorithm used for signing, and the claims.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">token</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">NewWithClaims</span>(<span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">SigningMethodHS256</span>, <span style="color:#a6e22e">claims</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Create the JWT string.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">tokenString</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">token</span>.<span style="color:#a6e22e">SignedString</span>(<span style="color:#a6e22e">secret</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>(), <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">tokenString</span>, <span style="color:#a6e22e">expirationTime</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Here we are creating a new cookie, which will store the valid JWT token.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">token</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">expiration</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Cookie</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Name</span> = <span style="color:#a6e22e">name</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Value</span> = <span style="color:#a6e22e">token</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Expires</span> = <span style="color:#a6e22e">expiration</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Path</span> = <span style="color:#e6db74">&#34;/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Http-only helps mitigate the risk of client side script accessing the protected cookie.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">HttpOnly</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SetCookie</span>(<span style="color:#a6e22e">cookie</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Purpose of this cookie is to store the user&#39;s name.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">expiration</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Cookie</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Value</span> = <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Expires</span> = <span style="color:#a6e22e">expiration</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Path</span> = <span style="color:#e6db74">&#34;/&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SetCookie</span>(<span style="color:#a6e22e">cookie</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// JWTErrorChecker will be executed when user try to access a protected path.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">JWTErrorChecker</span>(<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Redirects to the signIn form.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After finishing the main JWT token functionality, let&rsquo;s add the SignIn controllers, which will handle user authentication.
First, we need to add the new routers inside <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>    <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;/user/signin&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">SignInForm</span>()).<span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;userSignInForm&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">POST</span>(<span style="color:#e6db74">&#34;/user/signin&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">SignIn</span>())
</span></span></code></pre></div><p>In the code below I created a <code>user</code> package with the user structure and a function that loads a dummy user from imaginary database. We gonna need it in our controllers to process and validate user data.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">user</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/crypto/bcrypt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Password</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;password&#34; form:&#34;password&#34;`</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Name</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;name&#34; form:&#34;name&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">LoadTestUser</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Just for demonstration purpose, we create a user with the encrypted &#34;test&#34; password.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// In real-world applications, you might load the user from the database by specific parameters (email, username, etc.)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">hashedPassword</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">GenerateFromPassword</span>([]byte(<span style="color:#e6db74">&#34;test&#34;</span>), <span style="color:#ae81ff">8</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">Password</span>: string(<span style="color:#a6e22e">hashedPassword</span>), <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;Test user&#34;</span>}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After this, we will create a <code>controllers</code> package, where we add <code>SignInForm()</code> and <code>SignIn()</code> functions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/auth&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;golang.org/x/crypto/bcrypt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;html/template&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;path&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// SignInForm responsible for signIn Form rendering.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignInForm</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">fp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">fp</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#66d9ef">nil</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// SignIn will be executed after SignInForm submission.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignIn</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Load our &#34;test&#34; user.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">storedUser</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">LoadTestUser</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Initiate a new User struct.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Parse the submitted data and fill the User struct with the data from the SignIn form.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Bind</span>(<span style="color:#a6e22e">u</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Compare the stored hashed password, with the hashed version of the password that was received.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">CompareHashAndPassword</span>([]byte(<span style="color:#a6e22e">storedUser</span>.<span style="color:#a6e22e">Password</span>), []byte(<span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Password</span>)); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// If the two passwords don&#39;t match, return a 401 status.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Password is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If password is correct, generate tokens and set cookies.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">storedUser</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Token is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, in the <code>/templates</code> folder we need to create a <code>signIn.html</code> template with the simple SignIn form:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE html&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;en&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">form</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;w-full&#34;</span> <span style="color:#a6e22e">method</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post&#34;</span> <span style="color:#a6e22e">action</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/user/signin&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">label</span> <span style="color:#a6e22e">for</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span>&gt;Password:&lt;/<span style="color:#f92672">label</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">input</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;submit&#34;</span>&gt;Sign In&lt;/<span style="color:#f92672">button</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">form</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Let&rsquo;s also modify the <code>admin</code> controller in <code>controllers/admin.go</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Admin</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Gets user cookie.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">userCookie</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Cookie</span>(<span style="color:#e6db74">&#34;user&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">String</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;Hi, %s! You have been authenticated!&#34;</span>, <span style="color:#a6e22e">userCookie</span>.<span style="color:#a6e22e">Value</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, we need to add a JWT Middleware to the adminGroup path inside the <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Read more about JWT Middleware here: https://echo.labstack.com/middleware/jwt</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">middleware</span>.<span style="color:#a6e22e">JWTWithConfig</span>(<span style="color:#a6e22e">middleware</span>.<span style="color:#a6e22e">JWTConfig</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Claims</span>:                  <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">Claims</span>{},
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">SigningKey</span>:              []byte(<span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GetJWTSecret</span>()),
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">TokenLookup</span>:             <span style="color:#e6db74">&#34;cookie:access-token&#34;</span>, <span style="color:#75715e">// &#34;&lt;source&gt;:&lt;name&gt;&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">ErrorHandlerWithContext</span>: <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">JWTErrorChecker</span>,
</span></span><span style="display:flex;"><span>    }))
</span></span></code></pre></div><p>After that, execute <code>go run main.go</code>, to run the server, and go to <code>/admin</code> path. You will be redirected to the <code>/user/signin</code> path, because you need to be authenticated to access it. That is exactly what we need! Just enter <code>test</code> password and click on <code>Sign In</code> button. You will see this message: <code>Hi, Test user! You have been authenticated!</code> Awesome!</p>
<p>As you remember earlier, we set expiration time for the token: <code>expirationTime := time.Now().Add(1 * time.Hour)</code> It means, that after 1 hour user will be automatically logged-out. This is something what we want to prevent, especially if user is still active and doing some work on our resource.
This is possible to solve, by introducing a Refresh token. This token will have a much longer life-time and will be used for refreshing the Access token.
Let&rsquo;s modify our previous code.</p>
<p>First of all, we need to declare a secret for the Refresh token and cookie name to store the generated JWT. I will store it in a constant, but in the real-world applications please use environment variables for security reasons.</p>
<p>Inside <code>auth</code> package (/auth/auth.go) we need to add these modifications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessTokenCookieName</span>  = <span style="color:#e6db74">&#34;access-token&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">refreshTokenCookieName</span> = <span style="color:#e6db74">&#34;refresh-token&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtSecretKey</span> = <span style="color:#e6db74">&#34;some-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtRefreshSecretKey</span> = <span style="color:#e6db74">&#34;some-refresh-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetRefreshJWTSecret</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">jwtRefreshSecretKey</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">accessTokenCookieName</span>, <span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// We generate here a new refresh token and saving it to the cookie.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">refreshToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateRefreshToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">refreshTokenCookieName</span>, <span style="color:#a6e22e">refreshToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateRefreshToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the expiration time of the token - 24 hours.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">expirationTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">24</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">expirationTime</span>, []byte(<span style="color:#a6e22e">GetRefreshJWTSecret</span>()))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>At this point, when the user is signing-in, we generate 2 tokens: access and refresh. We still need to add logic for updating the access token, if the user is still active. For that, we can add a middleware, where we can check how much time is left for the user&rsquo;s access token, and if this time is less than some period of time (in this example it&rsquo;s 15 mins) we can generate the new tokens, by providing a valid refresh token.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// TokenRefresherMiddleware middleware, which refreshes JWT tokens if the access token is about to expire.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TokenRefresherMiddleware</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span>) <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If the user is not authenticated (no user token data in the context), don&#39;t do anything.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;user&#34;</span>) <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">next</span>(<span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Gets user token from the context.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;user&#34;</span>).(<span style="color:#f92672">*</span><span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">Token</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">claims</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Claims</span>.(<span style="color:#f92672">*</span><span style="color:#a6e22e">Claims</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// We ensure that a new token is not issued until enough time has elapsed.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// In this case, a new token will only be issued if the old token is within</span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 15 mins of expiry.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Unix</span>(<span style="color:#a6e22e">claims</span>.<span style="color:#a6e22e">ExpiresAt</span>, <span style="color:#ae81ff">0</span>).<span style="color:#a6e22e">Sub</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()) &lt; <span style="color:#ae81ff">15</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Gets the refresh token from the cookie.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">rc</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Cookie</span>(<span style="color:#a6e22e">refreshTokenCookieName</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">rc</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Parses token and checks if it valid.</span>
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">tkn</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">ParseWithClaims</span>(<span style="color:#a6e22e">rc</span>.<span style="color:#a6e22e">Value</span>, <span style="color:#a6e22e">claims</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">token</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">Token</span>) (<span style="color:#66d9ef">interface</span>{}, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">return</span> []byte(<span style="color:#a6e22e">GetRefreshJWTSecret</span>()), <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>				})
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">ErrSignatureInvalid</span> {
</span></span><span style="display:flex;"><span>						<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>)
</span></span><span style="display:flex;"><span>					}
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">tkn</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">tkn</span>.<span style="color:#a6e22e">Valid</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#75715e">// If everything is good, update tokens.</span>
</span></span><span style="display:flex;"><span>					<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>{
</span></span><span style="display:flex;"><span>						<span style="color:#a6e22e">Name</span>:  <span style="color:#a6e22e">claims</span>.<span style="color:#a6e22e">Name</span>,
</span></span><span style="display:flex;"><span>					}, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">next</span>(<span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, we have to attach our middleware to the <code>adminGroup</code> router inside the <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	<span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">TokenRefresherMiddleware</span>)
</span></span></code></pre></div><p>You can run the server again and experiment, how does it work. As an example, you can change the access token lifetime to 1min and investigate how the jwt cookies behave.</p>
<p>That was pretty much it. I hope this article was helpful for you.</p>
<p>The complete source code you can found <a href="https://github.com/alexsergivan/blog-examples/tree/master/authentication">here</a>.</p>
<p>Authentication is only the first half. For deciding what an authenticated user is allowed to reach, see <a href="/posts/how-to-control-router-access-permissions-in-go-web-apps/">how to control router access permissions in Go web apps</a>; for keeping one client from consuming everyone&rsquo;s capacity, <a href="/posts/rate-limiting-go-apis/">rate limiting Go APIs</a>. And to tell the user what actually happened after a failed login, <a href="/posts/how-to-show-flash-messages-in-go-echo/">how to show flash messages in Go web applications</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>How to Control Router Access Permissions in Go Web Apps</title>
      <link>https://webdevstation.com/posts/how-to-control-router-access-permissions-in-go-web-apps/</link>
      <pubDate>Wed, 23 Dec 2020 19:10:56 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-control-router-access-permissions-in-go-web-apps/</guid>
      <description>Learn how to implement URL-based access control and route permissions in Go web applications using the chi router middleware. A practical guide to securing your Go…</description>
      <content:encoded><![CDATA[<p>In this post I&rsquo;m going to describe how can we limit user access to the specific url in golang web application.
I will use <a href="https://github.com/go-chi/chi">chi</a> router - a lightweight, idiomatic and composable router for building
Go HTTP services.</p>
<p>Let&rsquo;s create our main package.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/go-chi/chi&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">homePageHandler</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/admin&#34;</span>, <span style="color:#a6e22e">adminPageHandler</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:3000&#34;</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">homePageHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is home page&#34;</span>)) 
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">adminPageHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is admin page&#34;</span>)) 
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After this, if we go to the <code>/admin</code> page, we will see &ldquo;This is admin page&rdquo;.</p>
<p>Now, let&rsquo;s make this path accessible only for admin.</p>
<p>We have to replace</p>
<p><code>r.Get(&quot;/admin&quot;, adminPageHandler)</code>
With
<code>r.Mount(&quot;/admin&quot;, adminRouter())</code></p>
<p>Mount attaches another http.Handler or chi Router as a subrouter along a routing path.</p>
<p>Then, we have to attach middleware inside adminRouter() function.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">adminRouter</span>() <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Middleware with access rules for router.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">AdminOnly</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">adminPageHandler</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">r</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In this middleware we have a simple check is user authorized to access this page or not.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">AdminOnly</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If user is admin, allows access.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">IsLoggedInAdmin</span>(<span style="color:#a6e22e">r</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Otherwise, 403.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusText</span>(<span style="color:#ae81ff">403</span>), <span style="color:#ae81ff">403</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In sake of demonstration, I&rsquo;m going just to use a random bool function to decide is used admin or not. You can modify this function according to your user authentication model.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">IsLoggedInAdmin</span>(<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Float32</span>() &lt; <span style="color:#ae81ff">0.5</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And that&rsquo;s it. Looks really simple, Isn&rsquo;t it?</p>
<p>Let&rsquo;s go to to the <code>/admin</code> page again.</p>
<p>As you see, now, sometimes (depends on our random decider), user has no access to this page anymore.</p>
<p>You can find source code <a href="https://github.com/alexsergivan/blog-examples/blob/master/route-auth/main.go">here</a></p>
<p>This builds directly on the pattern from <a href="/posts/go-middleware-example/">Go middleware example: how to alter a handler result</a>. For the authentication half of the problem, see <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>Go middleware example. How to alter a handler result</title>
      <link>https://webdevstation.com/posts/go-middleware-example/</link>
      <pubDate>Sun, 20 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/go-middleware-example/</guid>
      <description>Learn how to implement Go middleware to intercept and modify HTTP handler responses with practical examples using chi router, demonstrating request/response…</description>
      <content:encoded><![CDATA[<p>Let&rsquo;s imagine a situation when you want to alter the result, returned by some http handler to the client.
Fortunately, Golang provides an easy mechanism for that, called a middleware.
I&rsquo;m going to dive directly to the source code, to save your time.</p>
<p>Imagine, we have this simple web server (here I&rsquo;m using a chi router):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;bytes&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;github.com/go-chi/chi&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;io&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">myFirstHandler</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:3000&#34;</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">myFirstHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is a main page&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>When we run this application and visit a frontpage htttp://localhost:3000/, we can see this:
<img src="/images/16122020/1.png" alt="Browser showing the JSON response returned by myFirstHandler on the front page" title="Response from the original Go handler"></p>
<p>Now we got a new requirement to create another handler which should get all response data
from myFirstHandler and add some modification on top.</p>
<p>We can do it easily in this way:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Adds a new router handler with a middleware myMiddleware.  </span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">With</span>(<span style="color:#a6e22e">myMiddleware</span>).<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/other&#34;</span>, <span style="color:#a6e22e">myFirstHandler</span>) 
</span></span></code></pre></div><p>To be able to read a response from other handler, we have to implement our own <code>ResponseWriter</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyResponseWriter</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">buf</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Buffer</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Here we are implementing a Write() function from ResponseWriter with our custom instructions. </span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">myrw</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">MyResponseWriter</span>) <span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">p</span> []<span style="color:#66d9ef">byte</span>) (<span style="color:#66d9ef">int</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">myrw</span>.<span style="color:#a6e22e">buf</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">p</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, let&rsquo;s write our middleware:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">myMiddleware</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Create a response writer:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">myResponseWriter</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">MyResponseWriter</span>{
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">ResponseWriter</span>: <span style="color:#a6e22e">w</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">buf</span>:            <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Buffer</span>{},
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Here we are pssing our custom response writer to the next http handler.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">myResponseWriter</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Here we are adding our custom stuff to the response, which we received after http handler execution. </span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">myResponseWriter</span>.<span style="color:#a6e22e">buf</span>.<span style="color:#a6e22e">WriteString</span>(<span style="color:#e6db74">&#34; and some additional modifications&#34;</span>)
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// And, finally, we are copiing everything back to the original response writer.  if _, err := io.Copy(w, myResponseWriter.buf); err != nil {</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">log</span>.<span style="color:#960050;background-color:#1e0010">`</span><span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Failed to send out response: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>   })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, if we run our server again and go to <code>/other</code> path, we will see this:
<img src="/images/16122020/2.png" alt="Browser showing the same response after the middleware appended its extra field" title="Response after the Go middleware altered it"></p>
<p>This was a silly example, which will never happen in real life, but, I hope you got an overview how you can play
with http handlers and middlewares.</p>
<p>The source code you can found in this repository: <a href="https://github.com/alexsergivan/blog-examples/tree/master/middleware">https://github.com/alexsergivan/blog-examples/tree/master/middleware</a></p>
<p>Middleware is where a lot of cross-cutting concerns end up living. Three I have written about since: <a href="/posts/how-to-control-router-access-permissions-in-go-web-apps/">controlling router access permissions</a>, <a href="/posts/rate-limiting-go-apis/">rate limiting your API</a>, and <a href="/posts/structured-logging-in-go-with-slog/">putting a request-scoped logger in the context</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>How to make Nginx cache cookie aware</title>
      <link>https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</link>
      <pubDate>Tue, 15 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</guid>
      <description>Learn how to configure Nginx to cache responses based on specific cookie values, enabling proper A/B testing and personalized content delivery while maintaining…</description>
      <content:encoded><![CDATA[<p>In this post, I&rsquo;m going to describe how we can configure nginx to be able to cache responses based on the specific cookie value.</p>
<p>Let&rsquo;s imagine a situation when you want to do an A/B test on your website, where 50% of users should see new headline text on the page. Other 50% of visitors will continue see the old page. In this case, all your server-side manipulation about splitting users to different versions will be ignored by nginx cache (of course, if you have it).</p>
<p>It happens because nginx, by default, has this configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri;
</span></span></code></pre></div><p>So, it will use the same cache key for all users, who requests a page with the same url.</p>
<p>Luckily, nginx allows us easily to customize proxy_cache_key! What we need to do, it&rsquo;s just to add a specific cookie to this key:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri$cookie_MY_COOKIE_NAME;
</span></span></code></pre></div><p>And that&rsquo;s it! After this, if userA has MY_COOKIE_NAME=A and userB has MY_COOKIE_NAME=B they will receive different versions of page by the same url.</p>
<p>If you need more complex behaviour, where you won&rsquo;t use hardcoded cookie name in your nginx config, you can do something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#e6db74">(</span>$http_cookie ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">&#34;ab_(.*?)=([\w-]+)&#34;</span> <span style="color:#e6db74">)</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">set</span> $abcookie $1$2;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri$abcookie; 
</span></span></code></pre></div><p>As you can see, you can use $http_cookie and generate proxy_cache_key based on specific cookie patterns.
In this concrete example we check if http cookies contains cookie with regex <code>pattern ab_(.*?)=([\w-]+)</code> and if this cookie exists, we generate new variable for proxy_cache_key`.</p>
<p>Caching at the proxy is only one layer. For the one inside your Go process, see <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, and for shaving latency off the browser side, <a href="/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/">103 Early Hints in Go</a>.</p>]]></content:encoded>
      <category>DevOps</category>
      <category>Web Development</category>
    </item>
  </channel>
</rss>
