<?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>Openapi on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/openapi/</link>
    <description>Recent content in Openapi on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 19 Jan 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/openapi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to create a Golang Web Application using Fizz</title>
      <link>https://haseebmajid.dev/posts/2021-01-19-how-to-create-a-golang-web-application-using-fizz/</link>
      <pubDate>Tue, 19 Jan 2021 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2021-01-19-how-to-create-a-golang-web-application-using-fizz/</guid>
      <description>&lt;h1 id=&#34;background&#34;&gt;Background&lt;/h1&gt;
&lt;p&gt;A bit of background before we start the article. When I develop a Python web service I use the
&lt;a href=&#34;https://github.com/zalando/connexion&#34;&gt;Connexion library created by Zalando&lt;/a&gt;. It&amp;rsquo;s a great library which is built on top of
Flask. It uses an OpenAPI Specification (OAS) file to handle input validation and routing for you. Therefore reducing the boilerplate code you need to write.&lt;/p&gt;
&lt;p&gt;The main advantage of this is that we have a design-first approach to developing our API. We fully define the
OAS then develop the code/web service. This also keeps the OAS up to date, helping to mitigate the issue of the
code/documentation getting out of date. Especially when you share the OAS with other people (clients) to use. The last
thing you want to do is give them an out-of-date file.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="background">Background</h1>
<p>A bit of background before we start the article. When I develop a Python web service I use the
<a href="https://github.com/zalando/connexion">Connexion library created by Zalando</a>. It&rsquo;s a great library which is built on top of
Flask. It uses an OpenAPI Specification (OAS) file to handle input validation and routing for you. Therefore reducing the boilerplate code you need to write.</p>
<p>The main advantage of this is that we have a design-first approach to developing our API. We fully define the
OAS then develop the code/web service. This also keeps the OAS up to date, helping to mitigate the issue of the
code/documentation getting out of date. Especially when you share the OAS with other people (clients) to use. The last
thing you want to do is give them an out-of-date file.</p>
<p>Anyways short story aside, recently I started learning Golang and developing a simple CRUD web service using Gin.
However, I discovered (at least at the time of writing) there was no equivalent library to Connexion. The closest
library I could find was Fizz.</p>
<h2 id="what-is-fizz">What is Fizz?</h2>
<p>Fizz almost works the opposite way Connexion does. It generates an OAS
file from our code. Now again I prefer the Connexion approach because we just use
the OAS file we created at the beginning of the project. However, this is the next best thing.</p>
<p>What I ended up doing was creating an OAS by hand. Then implementing that OAS using Golang and letting Fizz
auto-generate the &ldquo;new&rdquo; OAS. This &ldquo;new&rdquo; OAS is the one that gets shared with clients and is kept up to date.
In theory the OAS I defined manually can now be deleted as it&rsquo;s not required anymore.</p>
<p>This solves the problem of our code getting out-of-date with the specification.
Fizz also uses other libraries behind the scenes to help us reduce the boilerplate code similiar to
how Connexion works.</p>
<h1 id="web-service">Web Service</h1>
<p>Now onto the real meat and potatoes of this article. We will create three different endpoints:</p>
<ul>
<li>GET /healthcheck: Checks if the application is healthy or not</li>
<li>GET /pet/{name}: Get information about a single pet</li>
<li>PUT /pet/{name}: Update information about a single pet</li>
</ul>
<h2 id="structure">Structure</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"></code></pre></div><p>Our project will follow the structure shown above. We will go (no pun intended 🤷) over what each of the folder &ldquo;do&rdquo;.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Core Code</summary>
  
  <p>Since this example application is so simple we don&rsquo;t have a <code>core</code> folder but for more complicated
applications you should probably add another folder inline with the <code>server</code> folder. For example, this could include code that interacts with the database.</p>
<p>This helps to de-couple the application&rsquo;s various layers. You could, for example, remove the web service part in the <code>server</code> folder and
turn into a CLI application at a later date. Using the core code you already have.</p>

</details>

<h2 id="dependencies">Dependencies</h2>
<p>The main dependency for this project is <a href="https://github.com/wI2L/fizz">Fizz</a>. Simply run <code>go get github.com/wI2L/fizz</code> to
install it.</p>
<h2 id="internal">internal</h2>
<p>The main logic of our web service will be stored within the <code>internal</code> folder.</p>
<h3 id="server">server</h3>
<p>This folder contains all the logic related to the web service itself. This will include
models (data structure returned to the client) and the controllers, which are functions
that will handle the various requests sent by clients. They act as an &ldquo;interface&rdquo; to our application.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Fizz Routing</summary>
  
  The Fizz library abstracts away routing partially for us, more on this later.
</details>

<h4 id="models">models</h4>
<p>This folder contains all the data structure and data types that will be received by the application
from the client or sent back to the client from the application. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">models</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Pet</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Name</span><span class="w">  </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Price</span><span class="w"> </span><span class="kt">int</span><span class="w">    </span><span class="s">`json:&#34;price&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Breed</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;breed&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>This will be the object sent back to the client when they request to get a pet. Note the use of struct tags
<code>json:&quot;name&quot;</code>. When the data is unmarshaled from JSON to this struct (again we will see how this done later) the <code>Name</code>
field will look for the <code>name</code> field in the JSON file. Later on we will see why we need to specify struct tags and not
just us being explicit.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">models</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">PetParams</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`query:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Note the struct tag in this example is <code>query</code> and not <code>json</code> because it&rsquo;s used as a query parameter.
We also have one final type of model to take a look at:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">models</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// PetsInput is the body data and params combined into a single struct.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">PetInput</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">PetParams</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">Pet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>This model is used when we need to pass both a Pet struct in the body of a request and also a query parameter. Again
we will see exactly how we use this model a bit later.</p>
<h4 id="controllers">controllers</h4>
<p>The controllers folder contains the main web service logic for the application. It contains the one function for every
route/endpoint you have in your application. Let&rsquo;s take a look at the maintenance controller first</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">controllers</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;time&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gin-gonic/gin&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/juju/errors&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;gitlab.com/hmajid2301/articles/example-fizz-project/internal/server/models&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">Healthcheck</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">models</span><span class="p">.</span><span class="nx">Healthcheck</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">host</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;example.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">port</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;80&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">timeout</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">healthy</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">net</span><span class="p">.</span><span class="nf">DialTimeout</span><span class="p">(</span><span class="s">&#34;tcp&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">host</span><span class="o">+</span><span class="s">&#34;:&#34;</span><span class="o">+</span><span class="nx">port</span><span class="p">,</span><span class="w"> </span><span class="nx">timeout</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">healthy</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">models</span><span class="p">.</span><span class="nx">Healthcheck</span><span class="p">{},</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;Healthcheck Failed!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">models</span><span class="p">.</span><span class="nx">Healthcheck</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Message</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;The API is healthy.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>So we have defined a new function, which receives a single argument the gin context (which we don&rsquo;t use, hence the <code>_</code>).
This function returns the health check model. It simply checks if we can connect to
<code>example.com:80</code> (on port 80).</p>
<p>Fizz uses the <a href="https://github.com/loopfz/gadgeto/tree/master/tonic">Tonic library</a> to assign function handlers to our
route.</p>
<blockquote>
<p>Package tonic handles path/query/body parameter binding in a single consolidated input object which allows you to remove all the boilerplate code that retrieves and tests the presence of various parameters. - Tonic README</p>
</blockquote>
<p>We need to specify two return types in the function definition because this function is a handler set using Tonic
Again we will see how we do this in the <code>routes.go</code> file. The first return type is a struct, which will be
returned to the client (marshalled into JSON). In the example above this is the <code>*models.Healthcheck</code>.
The second is an <code>error</code>, again we will see how errors are handled a bit later.</p>
<p>Let&rsquo;s now take a look at the pets controller.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">controllers</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/gin-gonic/gin&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;github.com/juju/errors&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;gitlab.com/hmajid2301/articles/example-fizz-project/internal/server/models&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">GetPet</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">params</span><span class="w"> </span><span class="o">*</span><span class="nx">models</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;bob&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{},</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">NotFoundf</span><span class="p">(</span><span class="s">&#34;Pet %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="p">:</span><span class="w">  </span><span class="s">&#34;bob&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Price</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Breed</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;bengal&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">UpdatePet</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">input</span><span class="w"> </span><span class="o">*</span><span class="nx">models</span><span class="p">.</span><span class="nx">PetInput</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;bob&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{},</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">NotFoundf</span><span class="p">(</span><span class="s">&#34;Pet %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The first function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">GetPet</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">params</span><span class="w"> </span><span class="o">*</span><span class="nx">models</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;bob&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{},</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">NotFoundf</span><span class="p">(</span><span class="s">&#34;Pet %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">params</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Name</span><span class="p">:</span><span class="w">  </span><span class="s">&#34;bob&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Price</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Breed</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;bengal&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><details
  class="notice info"
  open="true"
>
    <summary class="notice-title">JuJu Errors</summary>
  
  For throwing errors in this application we used <a href="https://github.com/juju/errors">juju&rsquo;s error library</a>.
</details>

<p>The main difference in this function is we pass in an extra parameter which is the query parameter <code>{name}</code>. The logic of this function is not very smart because
it expects the name of the pet to be <code>bob</code> in order to send a successful respone back to the client. Of course in
reality you would look in your data store for information about the pet.</p>
<p>The second function looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">UpdatePet</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">input</span><span class="w"> </span><span class="o">*</span><span class="nx">models</span><span class="p">.</span><span class="nx">PetInput</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;bob&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">Pet</span><span class="p">{},</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">NotFoundf</span><span class="p">(</span><span class="s">&#34;Pet %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">PetParams</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">input</span><span class="p">.</span><span class="nx">Pet</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span></code></pre></div><p>Again this is slightly different because the client sends both a HTTP body and a path query parameter. So the
<code>input</code> argument is a combination of two structs:</p>
<p>We can access the query parameter like so <code>input.PetParams.Name</code> and the pet&rsquo;s data like <code>input.Pet</code>. Note how we
use the name of the struct after <code>input</code>. This is how we can combine the body, query parameters and also the query
string into a single struct. The struct tags are really important as they let Tonic know what type of data that field is
i.e. <code>json</code> or <code>query</code> etc.</p>
<p>Again we can ignore the logic of the function itself. It&rsquo;s not supposed to be very complicated. Just more of an
example of how we can use Fizz, with more complicated HTTP requests.</p>
<h4 id="routesgo">routes.go</h4>
<p>This file is where we link the routes to their specific handler functions (using Tonic). This is also where we
provide most of the data that will be used to populate the OAS file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">NewRouter</span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">Fizz</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">engine</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">gin</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">engine</span><span class="p">.</span><span class="nf">Use</span><span class="p">(</span><span class="nx">cors</span><span class="p">.</span><span class="nf">Default</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fizzApp</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fizz</span><span class="p">.</span><span class="nf">NewFromEngine</span><span class="p">(</span><span class="nx">engine</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">infos</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">openapi</span><span class="p">.</span><span class="nx">Info</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Title</span><span class="p">:</span><span class="w">       </span><span class="s">&#34;Example API&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Description</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;The API definition for the Example API.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Version</span><span class="p">:</span><span class="w">     </span><span class="s">&#34;1.0.0&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/openapi.json&#34;</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">OpenAPI</span><span class="p">(</span><span class="nx">infos</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;json&#34;</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Group</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;maintenance&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Related to managing the maintenance of the API.&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/healthcheck&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Checks API is healthy.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">Healthcheck</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/pets:name&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Get a pet by name.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Pet Not Found&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">GetPet</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">PUT</span><span class="p">(</span><span class="s">&#34;/pets:name&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Update a pet.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">UpdatePet</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Errors</span><span class="p">())</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;fizz errors: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Errors</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">tonic</span><span class="p">.</span><span class="nf">SetErrorHook</span><span class="p">(</span><span class="nx">errHook</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Let&rsquo;s break this function down:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">engine</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">gin</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">engine</span><span class="p">.</span><span class="nf">Use</span><span class="p">(</span><span class="nx">cors</span><span class="p">.</span><span class="nf">Default</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fizzApp</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fizz</span><span class="p">.</span><span class="nf">NewFromEngine</span><span class="p">(</span><span class="nx">engine</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">infos</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">openapi</span><span class="p">.</span><span class="nx">Info</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Title</span><span class="p">:</span><span class="w">       </span><span class="s">&#34;Example API&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Description</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;The API definition for the Example API.&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Version</span><span class="p">:</span><span class="w">     </span><span class="s">&#34;1.0.0&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/openapi.json&#34;</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">OpenAPI</span><span class="p">(</span><span class="nx">infos</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;json&#34;</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>First, we create the Gin engine and share this with a new Fizz engine. Fizz just uses Gin behind the scenes.
Then we create an info struct, which stores the metadata for the generated OAS file. Then we add a new route
<code>/openapi.json</code>, which will serve the OAS file.</p>
<p>Note we could change the path if we wanted and serve
a YAML file as well <code>fizzApp.GET(&quot;/openapi&quot;, nil, fizzApp.OpenAPI(infos, &quot;yaml&quot;))</code>. Here we removed the
extension and changed the generated file so that we will serve the client a YAML file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Group</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;endpoints&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;All of the endpoints.&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/healthcheck&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Checks API is healthy.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">Healthcheck</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/pets:name&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Get a pet by name.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Pet Not Found&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">GetPet</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">PUT</span><span class="p">(</span><span class="s">&#34;/pets:name&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Update a pet.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">UpdatePet</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span></code></pre></div><p>Next, let&rsquo;s get to the part of the function where we define our routes. First, we create a group, this will group the
routes within the OAS (such as the tag).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="nx">group</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/pets:name&#34;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="nx">fizz</span><span class="p">.</span><span class="nx">OperationOption</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Summary</span><span class="p">(</span><span class="s">&#34;Get a pet by name.&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Server Error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">fizz</span><span class="p">.</span><span class="nf">Response</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">),</span><span class="w"> </span><span class="s">&#34;Pet Not Found&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{},</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">},</span><span class="w"> </span><span class="nx">tonic</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">GetPet</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">))</span><span class="w">
</span></span></span></code></pre></div><p>Next, let&rsquo;s take a look at how we define a new route. We add it to our existing group, then we give it some
information to add to the OAS such as summary. What responses we can get here I have defined the possible
errors. Note that because I prefer not to use magic numbers I have used the <code>http</code> package constants instead
of using numbers i.e. 404 -&gt; <code>http.StatusNotFound</code>. And of course the most important bit, the Tonic handler
where we tell this route what function to call when a client sends a request to this route. In this case, we
choose the <code>GetPet</code> function we mentioned earlier and on a successful response we return a <code>200</code> status code i.e.
<code>http.StatusOK</code>.</p>
<p>You can define whichever status code you want here such as an <code>http.StatusCreated</code> or <code>http.NoContent</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Errors</span><span class="p">())</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;fizz errors: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">.</span><span class="nf">Errors</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">tonic</span><span class="p">.</span><span class="nf">SetErrorHook</span><span class="p">(</span><span class="nx">errHook</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">fizzApp</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span></code></pre></div><p>The final part of the function checks if Fizz returned any errors and sets up the Tonic error hook. What to do if
any of the Tonic function handler return an error. As we saw earlier with some of the functions
returning errors.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">errHook</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">StatusText</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">e</span><span class="p">.(</span><span class="nx">tonic</span><span class="p">.</span><span class="nx">BindError</span><span class="p">);</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">switch</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsBadRequest</span><span class="p">(</span><span class="nx">e</span><span class="p">),</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsNotValid</span><span class="p">(</span><span class="nx">e</span><span class="p">),</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsNotSupported</span><span class="p">(</span><span class="nx">e</span><span class="p">),</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsNotProvisioned</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsForbidden</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusForbidden</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsMethodNotAllowed</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusMethodNotAllowed</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsNotFound</span><span class="p">(</span><span class="nx">e</span><span class="p">),</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsUserNotFound</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusNotFound</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsUnauthorized</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusUnauthorized</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsAlreadyExists</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusConflict</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">case</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">IsNotImplemented</span><span class="p">(</span><span class="nx">e</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusNotImplemented</span><span class="p">,</span><span class="w"> </span><span class="nx">e</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">models</span><span class="p">.</span><span class="nx">APIError</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Message</span><span class="p">:</span><span class="w"> </span><span class="nx">msg</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">code</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w">
</span></span></span></code></pre></div><p>This function receives an error since we are using juju error in our controller functions. We can then use the <code>isX</code>
the function provided by the library to check what kind of error we received. Using a switch statement we then determine
what type of HTTP status code to return to the client depending on the error thrown by the function. For example an <code>NotFoundError</code> means we return <code>http.StatusNotFound</code> (404).</p>
<p>Ok, that&rsquo;s the main part of our application so how do we start our web service?</p>
<h2 id="cmd">cmd</h2>
<p>In our cmd folder, we have the <code>main.go</code> file.</p>
<h3 id="maingo">main.go</h3>
<p>The <code>main.go</code> file, as is good practice in Golang, is used to start our application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;gitlab.com/hmajid2301/articles/example-fizz-project/internal/server&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">router</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">server</span><span class="p">.</span><span class="nf">NewRouter</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">srv</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Addr</span><span class="p">:</span><span class="w">    </span><span class="s">&#34;:8080&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">Handler</span><span class="p">:</span><span class="w"> </span><span class="nx">router</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">srv</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>In the root folder of our application run this command <code>go run cmd/example-fizz-project/main.go</code>. Then you should see something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go run cmd/example-fizz-project/main.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>GIN-debug<span class="o">]</span> <span class="o">[</span>WARNING<span class="o">]</span> Running in <span class="s2">&#34;debug&#34;</span> mode. Switch to <span class="s2">&#34;release&#34;</span> mode in production.
</span></span><span class="line"><span class="cl"> - using env:   <span class="nb">export</span> <span class="nv">GIN_MODE</span><span class="o">=</span>release
</span></span><span class="line"><span class="cl"> - using code:  gin.SetMode<span class="o">(</span>gin.ReleaseMode<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>GIN-debug<span class="o">]</span> GET    /openapi.json             --&gt; github.com/wI2L/fizz.<span class="o">(</span>*Fizz<span class="o">)</span>.OpenAPI.func1 <span class="o">(</span><span class="m">2</span> handlers<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>GIN-debug<span class="o">]</span> GET    /healthcheck              --&gt; github.com/wI2L/fizz.<span class="o">(</span>*RouterGroup<span class="o">)</span>.Handle.func1 <span class="o">(</span><span class="m">2</span> handlers<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>GIN-debug<span class="o">]</span> GET    /pets:name                --&gt; github.com/wI2L/fizz.<span class="o">(</span>*RouterGroup<span class="o">)</span>.Handle.func1 <span class="o">(</span><span class="m">2</span> handlers<span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>GIN-debug<span class="o">]</span> PUT    /pets:name                --&gt; github.com/wI2L/fizz.<span class="o">(</span>*RouterGroup<span class="o">)</span>.Handle.func1 <span class="o">(</span><span class="m">2</span> handlers<span class="o">)</span>
</span></span></code></pre></div><p>And voila you now have a working web service you have created using Fizz. That&rsquo;s it we have now built a web application with Fizz and Golang.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2021-01-19-how-to-create-a-golang-web-application-using-fizz/source_code">Example source code</a></li>
<li><a href="https://github.com/wI2L/fizz/">Fizz</a></li>
<li><a href="https://github.com/loopfz/gadgeto/tree/master/tonic">Tonic</a></li>
<li><a href="https://github.com/juju/errors">Juju&rsquo;s Errors</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
