<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Fluxy]]></title><description><![CDATA[Fluxy]]></description><link>https://blog.fluxy.net</link><generator>RSS for Node</generator><lastBuildDate>Thu, 28 May 2026 17:56:58 GMT</lastBuildDate><atom:link href="https://blog.fluxy.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[HTTP introduction to Go]]></title><description><![CDATA[This is a very basic tutorial on how to use http and sql in Go. It presumes that you have basic programming knowledge and that you have installed Go.
Hello World
To start with we need to create a new directory and initialize a new go module.
We creat...]]></description><link>https://blog.fluxy.net/http-introduction-to-go</link><guid isPermaLink="true">https://blog.fluxy.net/http-introduction-to-go</guid><category><![CDATA[Go Language]]></category><category><![CDATA[introduction]]></category><category><![CDATA[basics]]></category><category><![CDATA[devfest]]></category><category><![CDATA[DevFest23]]></category><dc:creator><![CDATA[Muhammad Yusuf Abdool Satar]]></dc:creator><pubDate>Fri, 27 Oct 2023 21:05:16 GMT</pubDate><content:encoded><![CDATA[<p>This is a very basic tutorial on how to use http and sql in Go. It presumes that you have basic programming knowledge and that you have installed Go.</p>
<h2 id="heading-hello-world">Hello World</h2>
<p>To start with we need to create a new directory and initialize a new go module.</p>
<p>We create a new directory called <code>http-intro</code> and change into it.</p>
<p>Then we need to initialize a new go module by running <code>go mod init github.com/fluxynet/http-intro</code>.</p>
<pre><code class="lang-bash">mkdir http-intro
<span class="hljs-built_in">cd</span> http-intro
go mod init http-intro
</code></pre>
<p>This will create a file called <code>go.mod</code> which contains the name of the module and the go version.</p>
<p>It will also contain dependencies as we add them.</p>
<p>The file <code>go.sum</code> will contain the checksums of the dependencies to ensure that everyone gets the correct version of the dependencies.</p>
<p>Create a file called <code>main.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    fmt.Fprintf(w, <span class="hljs-string">"Hello World!"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    http.HandleFunc(<span class="hljs-string">"/"</span>, handler)
    http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>)
}
</code></pre>
<p>The <code>handler</code> function takes two arguments, a <code>http.ResponseWriter</code> and a <code>*http.Request</code>.</p>
<p>The <code>http.ResponseWriter</code> is used to write the response to the client.</p>
<h2 id="heading-http-handlers">HTTP Handlers</h2>
<h3 id="heading-the-request-object">The request object</h3>
<p>The <code>*http.Request</code> contains information about the request.</p>
<p>We can use the <code>*http.Request</code> to get information about the request.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    fmt.Fprintf(w, <span class="hljs-string">"Hello World!\n"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"Method: %s\n"</span>, r.Method)
    fmt.Fprintf(w, <span class="hljs-string">"URL: %s\n"</span>, r.URL)
    fmt.Fprintf(w, <span class="hljs-string">"Protocol: %s\n"</span>, r.Proto)
    fmt.Fprintf(w, <span class="hljs-string">"Host: %s\n"</span>, r.Host)
    fmt.Fprintf(w, <span class="hljs-string">"RemoteAddr: %s\n"</span>, r.RemoteAddr)
    fmt.Fprintf(w, <span class="hljs-string">"RequestURI: %s\n"</span>, r.RequestURI)
    fmt.Fprintf(w, <span class="hljs-string">"Path: %s\n"</span>, r.URL.Path)

    <span class="hljs-keyword">for</span> name := <span class="hljs-keyword">range</span> r.Header {
        fmt.Fprintf(w, <span class="hljs-string">"Header: %s: %s\n"</span>, name, r.Header.Get(name))
    }

    <span class="hljs-keyword">for</span> name, values := <span class="hljs-keyword">range</span> r.URL.Query() {
        <span class="hljs-keyword">for</span> _, value := <span class="hljs-keyword">range</span> values {
            fmt.Fprintf(w, <span class="hljs-string">"Query: %s: %s\n"</span>, name, value)
        }
    }

    <span class="hljs-keyword">for</span> _, cookie := <span class="hljs-keyword">range</span> r.Cookies() {
        fmt.Fprintf(w, <span class="hljs-string">"Cookie: %s: %s\n"</span>, cookie.Name, cookie.Value)
    }

    fmt.Fprintf(w, <span class="hljs-string">"Body: %s\n"</span>, r.Body)
}
</code></pre>
<p>Let us test the above code by running <code>go run main.go</code> and then open a browser and go to <code>http://localhost:8080</code>.</p>
<p>We can also test the code by sending direct http requests. For this purpose we will use the vscode extension <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client</a>.</p>
<p>Create a file called <code>requests.http</code> and add the following code:</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET http://localhost:8080/</span>
</code></pre>
<p>Then click on the <code>Send Request</code> link above the request.</p>
<p>We can add query parameters to the request.</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET http://localhost:8080/?name=apple&amp;color=red</span>
</code></pre>
<p>And cookies</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET http://localhost:8080/?name=apple&amp;color=red
Cookie</span>: user=john
</code></pre>
<p>We can also use different methods like <code>POST</code>, <code>PUT</code>, <code>PATCH</code> and <code>DELETE</code>.</p>
<pre><code class="lang-http"><span class="hljs-attribute">POST http://localhost:8080/

Hello World!</span>
</code></pre>
<h4 id="heading-the-request-body">The request body</h4>
<p>The <code>*http.Request</code> also contains a <code>Body</code> which is a <code>io.ReadCloser</code>.</p>
<p>We can read the body by using the <code>ioutil.ReadAll</code> function.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    body, err := ioutil.ReadAll(r.Body)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Fprintf(w, <span class="hljs-string">"Error reading body: %v"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    fmt.Fprintf(w, <span class="hljs-string">"Hello World!\n"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"Method: %s\n"</span>, r.Method)
    fmt.Fprintf(w, <span class="hljs-string">"URL: %s\n"</span>, r.URL)
    fmt.Fprintf(w, <span class="hljs-string">"Protocol: %s\n"</span>, r.Proto)
    fmt.Fprintf(w, <span class="hljs-string">"Host: %s\n"</span>, r.Host)
    fmt.Fprintf(w, <span class="hljs-string">"RemoteAddr: %s\n"</span>, r.RemoteAddr)
    fmt.Fprintf(w, <span class="hljs-string">"RequestURI: %s\n"</span>, r.RequestURI)
    fmt.Fprintf(w, <span class="hljs-string">"Header: %s\n"</span>, r.Header)
    fmt.Fprintf(w, <span class="hljs-string">"Body: %s\n"</span>, body)
}
</code></pre>
<p>We can also use the <code>json</code> package to decode the body into a struct.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Fruit <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
    Color <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"color"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    body, err := ioutil.ReadAll(r.Body)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Fprintf(w, <span class="hljs-string">"Error reading body: %v"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">var</span> fruit Fruit
    err = json.Unmarshal(body, &amp;fruit)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Fprintf(w, <span class="hljs-string">"Error unmarshaling body: %v"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    fmt.Fprintf(w, <span class="hljs-string">"Hello World!\n"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"Method: %s\n"</span>, r.Method)
    fmt.Fprintf(w, <span class="hljs-string">"URL: %s\n"</span>, r.URL)
    fmt.Fprintf(w, <span class="hljs-string">"Protocol: %s\n"</span>, r.Proto)
    fmt.Fprintf(w, <span class="hljs-string">"Host: %s\n"</span>, r.Host)
    fmt.Fprintf(w, <span class="hljs-string">"RemoteAddr: %s\n"</span>, r.RemoteAddr)
    fmt.Fprintf(w, <span class="hljs-string">"RequestURI: %s\n"</span>, r.RequestURI)
    fmt.Fprintf(w, <span class="hljs-string">"Header: %s\n"</span>, r.Header)
    fmt.Fprintf(w, <span class="hljs-string">"Fruit: %s\n"</span>, fruit)
}
</code></pre>
<p>We can test this by sending a <code>POST</code> request with the following body:</p>
<pre><code class="lang-http"><span class="hljs-attribute">POST http://localhost:8080/

{
    "name"</span>: "apple",
    "color": "red"
}
</code></pre>
<p>We can use different encodings like url encoded and form data.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    err := r.ParseForm()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Fprintf(w, <span class="hljs-string">"Error parsing form: %v"</span>, err)
        <span class="hljs-keyword">return</span>
    }
    fmt.Fprintf(w, <span class="hljs-string">"Hello World!\n"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"Method: %s\n"</span>, r.Method)
    fmt.Fprintf(w, <span class="hljs-string">"URL: %s\n"</span>, r.URL)
    fmt.Fprintf(w, <span class="hljs-string">"Protocol: %s\n"</span>, r.Proto)
    fmt.Fprintf(w, <span class="hljs-string">"Host: %s\n"</span>, r.Host)
    fmt.Fprintf(w, <span class="hljs-string">"RemoteAddr: %s\n"</span>, r.RemoteAddr)
    fmt.Fprintf(w, <span class="hljs-string">"RequestURI: %s\n"</span>, r.RequestURI)
    fmt.Fprintf(w, <span class="hljs-string">"Header: %s\n"</span>, r.Header)
    fmt.Fprintf(w, <span class="hljs-string">"Form: %s\n"</span>, r.Form)
}
</code></pre>
<p>We can test this by sending a <code>POST</code> request with the following body:</p>
<pre><code class="lang-http"><span class="hljs-attribute">POST http://localhost:8080/
Content-Type</span>: application/x-www-form-urlencoded

<span class="solidity">name<span class="hljs-operator">=</span>apple<span class="hljs-operator">&amp;</span>color<span class="hljs-operator">=</span>red</span>
</code></pre>
<h4 id="heading-the-response-object">The response object</h4>
<p>The <code>http.ResponseWriter</code> is used to write the response to the client.</p>
<p>We can use the <code>http.ResponseWriter</code> to specify the status code, set headers, send cookies and write the response body.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    http.SetCookie(w, &amp;http.Cookie{
        Name: <span class="hljs-string">"user"</span>,
        Value: <span class="hljs-string">"john"</span>,
        MaxAge: <span class="hljs-number">86400</span>,
    })

    <span class="hljs-comment">// status code must be set before writing the response body but after setting headers</span>
    w.WriteHeader(http.StatusCreated)
    fmt.Fprintf(w, <span class="hljs-string">`{"message": "Hello World!"}`</span>)

}
</code></pre>
<p>We can test this by sending a <code>GET</code> request with the following body:</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET http://localhost:8080/</span>
</code></pre>
<p>We can also use the <code>http.ResponseWriter</code> to send json responses.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Fruit <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
    Color <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"color"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)

    <span class="hljs-comment">// status code must be set before writing the response body but after setting headers</span>
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(Fruit{
        Name: <span class="hljs-string">"apple"</span>,
        Color: <span class="hljs-string">"red"</span>,
    })
}
</code></pre>
<h2 id="heading-data-persistence">Data Persistence</h2>
<p>Before moving to REST APIs let us look at how we can persist data.</p>
<p>Go has a built in package called <code>database/sql</code> which is a generic interface around SQL databases.</p>
<p>We will use the <code>database/sql</code> package to connect to a sqlite database.</p>
<p>We will also use the <code>github.com/mattn/go-sqlite3</code> package which is a sqlite driver for the <code>database/sql</code> package.</p>
<p>We will create a new file called <code>db.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>

    _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    initialise()
    read()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">initialise</span><span class="hljs-params">()</span></span> {
    db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, <span class="hljs-string">"fruits.db"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    sqlStmt := <span class="hljs-string">`
    CREATE TABLE IF NOT EXISTS fruits (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        color TEXT
    );
    `</span>
    _, err = db.Exec(sqlStmt)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    sqlStmt = <span class="hljs-string">`
    INSERT INTO fruits (name, color) VALUES
        ("apple", "red"),
        ("banana", "yellow"),
        ("grape", "purple"),
        ("orange", "orange"),
        ("strawberry", "red"),
        ("watermelon", "green");
    `</span>
    _, err = db.Exec(sqlStmt)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">read</span><span class="hljs-params">()</span></span> {
    rows, err := db.Query(<span class="hljs-string">"SELECT id, name, color FROM fruits"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> rows.Close()
    <span class="hljs-keyword">for</span> rows.Next() {
        <span class="hljs-keyword">var</span> id <span class="hljs-keyword">int</span>
        <span class="hljs-keyword">var</span> name <span class="hljs-keyword">string</span>
        <span class="hljs-keyword">var</span> color <span class="hljs-keyword">string</span>
        err = rows.Scan(&amp;id, &amp;name, &amp;color)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            log.Fatal(err)
        }
        fmt.Println(id, name, color)
    }
    err = rows.Err()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
}
</code></pre>
<h3 id="heading-repository-pattern">Repository Pattern</h3>
<p>We will use the repository pattern to abstract away the database.</p>
<p>This abstraction has the following benefits:</p>
<ul>
<li><p>We can easily switch databases</p>
</li>
<li><p>We can easily mock the database for testing</p>
</li>
<li><p>We can easily add caching</p>
</li>
<li><p>The code is easier to read and understand</p>
</li>
</ul>
<p>We will also add more structure to our code to make it easier to add functionality as we go along.</p>
<p>Let us start over with a file called <code>types.go</code>. This will contain all the types we will use.</p>
<h4 id="heading-types">Types</h4>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> httpintro

<span class="hljs-keyword">type</span> Fruit <span class="hljs-keyword">struct</span> {
    ID <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"id"`</span>
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
    Color <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"color"`</span>
}

<span class="hljs-keyword">type</span> FruitRepository <span class="hljs-keyword">interface</span> {
    Get(id <span class="hljs-keyword">int</span>) (*Fruit, error)
    GetAll() ([]Fruit, error)
    Create(fruit *Fruit) error
    Update(fruit *Fruit) error
    Delete(id <span class="hljs-keyword">int</span>) error
}
</code></pre>
<p><code>FruitRepository</code> is an interface which defines the methods we will use to persist <code>Fruit</code> objects.</p>
<h4 id="heading-errors">Errors</h4>
<p>Additionally we will create a file <code>errors.go</code> which will contain any errors we will use.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> httpintro

<span class="hljs-keyword">import</span> <span class="hljs-string">"errors"</span>

<span class="hljs-keyword">var</span> (
    ErrNotFound = errors.New(<span class="hljs-string">"not found"</span>)
)
</code></pre>
<h4 id="heading-implementation">Implementation</h4>
<p>Next let create a package called <code>repos</code> which will contain different repositories for our application.</p>
<p>Create a file as <code>repos/fruits-repo/fruits_repo.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> fruitsrepo

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro"</span>
    _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)

<span class="hljs-keyword">type</span> Repository <span class="hljs-keyword">struct</span> {
    db *sql.DB
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">New</span><span class="hljs-params">(db *sql.DB)</span> *<span class="hljs-title">Repository</span></span> {
    <span class="hljs-keyword">return</span> &amp;Repository{
        db: db,
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Repository)</span> <span class="hljs-title">Get</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*httpintro.Fruit, error)</span></span> {
    <span class="hljs-keyword">var</span> fruit httpintro.Fruit

    row := r.db.QueryRow(<span class="hljs-string">"SELECT id, name, color FROM fruits WHERE id = ?"</span>, id)
    <span class="hljs-keyword">if</span> err := row.Scan(&amp;fruit.ID, &amp;fruit.Name, &amp;fruit.Color); errors.Is(err, sql.ErrNoRows) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, httpintro.ErrNotFound
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    <span class="hljs-keyword">return</span> &amp;fruit, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Repository)</span> <span class="hljs-title">GetAll</span><span class="hljs-params">()</span> <span class="hljs-params">([]httpintro.Fruit, error)</span></span> {
    rows, err := r.db.Query(<span class="hljs-string">"SELECT id, name, color FROM fruits"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">defer</span> rows.Close()

    <span class="hljs-keyword">var</span> fruits []httpintro.Fruit

    <span class="hljs-keyword">for</span> rows.Next() {
        <span class="hljs-keyword">var</span> fruit httpintro.Fruit
        <span class="hljs-keyword">if</span> err = rows.Scan(&amp;fruit.ID, &amp;fruit.Name, &amp;fruit.Color); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
        fruits = <span class="hljs-built_in">append</span>(fruits, fruit)
    }

    <span class="hljs-keyword">return</span> fruits, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Repository)</span> <span class="hljs-title">Create</span><span class="hljs-params">(fruit *httpintro.Fruit)</span> <span class="hljs-title">error</span></span> {
    result, err := r.db.Exec(<span class="hljs-string">"INSERT INTO fruits (name, color) VALUES (?, ?)"</span>, fruit.Name, fruit.Color)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    id, err := result.LastInsertId()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    fruit.ID = <span class="hljs-keyword">int</span>(id)

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Repository)</span> <span class="hljs-title">Update</span><span class="hljs-params">(fruit *httpintro.Fruit)</span> <span class="hljs-title">error</span></span> {
    result, err := r.db.Exec(<span class="hljs-string">"UPDATE fruits SET name = ?, color = ? WHERE id = ?"</span>, fruit.Name, fruit.Color, fruit.ID)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    <span class="hljs-keyword">if</span> rowsAffected, err := result.RowsAffected(); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> rowsAffected == <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> httpintro.ErrNotFound
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Repository)</span> <span class="hljs-title">Delete</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> _, err := r.db.Exec(<span class="hljs-string">"DELETE FROM fruits WHERE id = ?"</span>, id); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<h4 id="heading-importing-data">Importing data</h4>
<p>Let us add an import functionality that will allow us to import data from a json file. It will also be an opportunity to see if our repository works.</p>
<p>It is common to have a <code>cmd</code> folder which in turn contains a folder for each command.</p>
<p>We will create a folder called <code>cmd</code> and a folder called <code>import</code> inside it.</p>
<p>Create a file called <code>cmd/import/main.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"os"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro"</span>
    fruitsrepo <span class="hljs-string">"github.com/fluxynet/httpintro/repos/fruits-repo"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    file, err := os.ReadFile(<span class="hljs-string">"fruits.json"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    <span class="hljs-keyword">var</span> fruits []httpintro.Fruit
    err = json.Unmarshal(file, &amp;fruits)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, <span class="hljs-string">"fruits.db"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    createTableSQL := <span class="hljs-string">`
    CREATE TABLE IF NOT EXISTS fruits (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        color TEXT
    );
    `</span>
    _, err = db.Exec(createTableSQL)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    repo := fruitsrepo.New(db)

    <span class="hljs-keyword">for</span> _, fruit := <span class="hljs-keyword">range</span> fruits {
        err = repo.Create(&amp;fruit)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            log.Fatal(err)
        }
    }

    fmt.Println(<span class="hljs-string">"Import successful"</span>)
}
</code></pre>
<p>Let us add another command to display all the fruits in the database. Create a file as <code>cmd/list/main.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro/repos/fruits-repo"</span>
    _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, <span class="hljs-string">"fruits.db"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    repo := fruitsrepo.New(db)

    fruits, err := repo.GetAll()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    <span class="hljs-keyword">for</span> _, fruit := <span class="hljs-keyword">range</span> fruits {
        fmt.Println(fruit)
    }
}
</code></pre>
<p>Let us test the import command by running <code>go run cmd/import/main.go</code> and then run <code>go run cmd/list/main.go</code>. First ensure that the <code>fruits.json</code> file exists.</p>
<p>You may use the following:</p>
<pre><code class="lang-json">[
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"apple"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"red"</span>
    },
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"banana"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"yellow"</span>
    },
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"grape"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"purple"</span>
    },
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"orange"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"orange"</span>
    },
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"strawberry"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"red"</span>
    },
    {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"watermelon"</span>,
        <span class="hljs-attr">"color"</span>: <span class="hljs-string">"green"</span>
    }
]
</code></pre>
<h2 id="heading-rest-api">REST API</h2>
<p>Now that we have a way to persist data let us create a REST API to interact with the data.</p>
<p>We will create a new package called <code>web/fruits-api</code> which will contain the REST API. The <code>web</code> package itself may be used to contain commonly used code for http applications.</p>
<p>Create a file called <code>web/fruits-api/fruits_api.go</code> and add the following code:</p>
<p>For the purpose of this tutorial we will use the <code>github.com/go-chi/chi</code> package to handle routing.</p>
<p>This package is not part of the standard library and can be installed by running <code>go get github.com/go-chi/chi/v5</code>. It provides a powerful router which supports url parameters, middlewares and subrouters.</p>
<h3 id="heading-helpers">Helpers</h3>
<p>We will create a file called <code>web/web.go</code> which will contain commonly used functions.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> web

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"net/http"</span>
)

<span class="hljs-keyword">type</span> ErrorResponse <span class="hljs-keyword">struct</span> {
    Error <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"error"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Error</span><span class="hljs-params">(w http.ResponseWriter, err error, status <span class="hljs-keyword">int</span>)</span></span> {
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(ErrorResponse{
        Error: err.Error(),
    })
}
</code></pre>
<h3 id="heading-api">API</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> fruitsapi

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"strconv"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro"</span>
    <span class="hljs-string">"github.com/fluxynet/httpintro/web"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
    _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)

<span class="hljs-keyword">type</span> API <span class="hljs-keyword">struct</span> {
    repo httpintro.FruitRepository
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">New</span><span class="hljs-params">(repo httpintro.FruitRepository)</span> *<span class="hljs-title">API</span></span> {
    <span class="hljs-keyword">return</span> &amp;API{
        repo: repo,
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">Route</span><span class="hljs-params">(r chi.Router)</span></span> {
    r.Get(<span class="hljs-string">"/"</span>, a.GetAll)
    r.Post(<span class="hljs-string">"/"</span>, a.Create)
    r.Get(<span class="hljs-string">"/{id}"</span>, a.Get)
    r.Put(<span class="hljs-string">"/{id}"</span>, a.Update)
    r.Delete(<span class="hljs-string">"/{id}"</span>, a.Delete)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">Get</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id, err := strconv.Atoi(chi.URLParam(r, <span class="hljs-string">"id"</span>))
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    fruit, err := a.repo.Get(id)
    <span class="hljs-keyword">if</span> errors.Is(err, httpintro.ErrNotFound) {
        web.Error(w, err, http.StatusNotFound)
        <span class="hljs-keyword">return</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusInternalServerError)
        <span class="hljs-keyword">return</span>
    }

    json.NewEncoder(w).Encode(fruit)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">GetAll</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    fruits, err := a.repo.GetAll()
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusInternalServerError)
        <span class="hljs-keyword">return</span>
    }

    json.NewEncoder(w).Encode(fruits)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">Create</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-keyword">var</span> fruit httpintro.Fruit
    err := json.NewDecoder(r.Body).Decode(&amp;fruit)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    err = a.repo.Create(&amp;fruit)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusInternalServerError)
        <span class="hljs-keyword">return</span>
    }

    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(fruit)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">Update</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id, err := strconv.Atoi(chi.URLParam(r, <span class="hljs-string">"id"</span>))
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-keyword">var</span> fruit httpintro.Fruit
    err = json.NewDecoder(r.Body).Decode(&amp;fruit)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    fruit.ID = id

    err = a.repo.Update(&amp;fruit)
    <span class="hljs-keyword">if</span> errors.Is(err, httpintro.ErrNotFound) {
        web.Error(w, err, http.StatusNotFound)
        <span class="hljs-keyword">return</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusInternalServerError)
        <span class="hljs-keyword">return</span>
    }

    json.NewEncoder(w).Encode(fruit)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *API)</span> <span class="hljs-title">Delete</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id, err := strconv.Atoi(chi.URLParam(r, <span class="hljs-string">"id"</span>))
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    err = a.repo.Delete(id)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        web.Error(w, err, http.StatusInternalServerError)
        <span class="hljs-keyword">return</span>
    }

    w.WriteHeader(http.StatusNoContent)
}
</code></pre>
<p>Next we will create a new command called <code>cmd/server</code> which will start a http server.</p>
<p>Create a file called <code>cmd/server/main.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>

    fruitsrepo <span class="hljs-string">"github.com/fluxynet/httpintro/repos/fruits-repo"</span>
    fruitsapi <span class="hljs-string">"github.com/fluxynet/httpintro/web/fruits-api"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
    _ <span class="hljs-string">"github.com/mattn/go-sqlite3"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    db, err := sql.Open(<span class="hljs-string">"sqlite3"</span>, <span class="hljs-string">"fruits.db"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    repo := fruitsrepo.New(db)
    api := fruitsapi.New(repo)

    r := chi.NewRouter()
    r.Route(<span class="hljs-string">"/fruits"</span>, api.Route)

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, r)
}
</code></pre>
<p>We can use the following <code>requests.http</code> file to test the API:</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET http://localhost:8080/fruits

###

POST http://localhost:8080/fruits
Content-Type</span>: application/json

<span class="graphql">{
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"cherry"</span>,
    <span class="hljs-string">"color"</span>: <span class="hljs-string">"red"</span>
}

<span class="hljs-comment">###</span>

GET <span class="hljs-symbol">http:</span>//<span class="hljs-symbol">localhost:</span><span class="hljs-number">8080</span>/fruits/<span class="hljs-number">1</span>

<span class="hljs-comment">###</span>

PUT <span class="hljs-symbol">http:</span>//<span class="hljs-symbol">localhost:</span><span class="hljs-number">8080</span>/fruits/<span class="hljs-number">1</span>

{
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"apple"</span>,
    <span class="hljs-string">"color"</span>: <span class="hljs-string">"green"</span>
}

<span class="hljs-comment">###</span>

DELETE <span class="hljs-symbol">http:</span>//<span class="hljs-symbol">localhost:</span><span class="hljs-number">8080</span>/fruits/<span class="hljs-number">1</span></span>
</code></pre>
<h3 id="heading-middlewares">Middlewares</h3>
<p>Middlewares are functions that are executed before or after a request is handled. They are useful for adding functionality like logging, authentication, authorization, rate limiting, etc.</p>
<p>Chi has a built in middleware called <code>chi/middleware.Logger</code> which logs the request and response.</p>
<p>We can add it to our server by adding the following code:</p>
<pre><code class="lang-go">r.Use(middleware.Logger)
</code></pre>
<p>We can also create our own middleware.</p>
<p>Let us create a middleware that will check if the request has a valid api key.</p>
<p>Create a file called <code>web/middlewares/api_key.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> middlewares

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">APIKey</span><span class="hljs-params">(apiKey <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
        <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
            key := r.Header.Get(<span class="hljs-string">"X-API-Key"</span>)
            <span class="hljs-keyword">if</span> key != apiKey {
                http.Error(w, <span class="hljs-string">"Invalid API Key"</span>, http.StatusUnauthorized)
                <span class="hljs-keyword">return</span>
            }
            next.ServeHTTP(w, r)
        })
    }
}
</code></pre>
<p>We can use the middleware by adding the following code:</p>
<pre><code class="lang-go">r.Use(middlewares.APIKey(<span class="hljs-string">"secret"</span>))
</code></pre>
<h2 id="heading-testing">Testing</h2>
<p>Testing is an important part of software development. It allows us to ensure that our code works as expected and that we do not break anything when we make changes.</p>
<p>Go has a built in testing package called <code>testing</code> which we will use to write our tests.</p>
<p>We will be writing unit tests for the <code>fruitsapi</code> package.</p>
<h3 id="heading-mocking">Mocking</h3>
<p>Our api has a dependency on the <code>fruitsrepo</code> package which we will mock.</p>
<p>We will use the mockery package to generate mocks for our interfaces.</p>
<p>In our root directory we will create a file called <code>tools.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> httpintro

<span class="hljs-comment">//go:generate go install github.com/vektra/mockery/v2@latest</span>
<span class="hljs-comment">//go:generate mockery --name=FruitRepository --output=mocks/repos/fruits-repo --outpkg=fruitsrepo --filename=fruits_repo.go</span>
</code></pre>
<p>The above contains two directives which will be used by the <code>go generate</code> command:</p>
<ul>
<li><p>The first line will install the mockery package.</p>
</li>
<li><p>The second line will generate a mock for the <code>FruitRepository</code> interface and place it in the <code>mocks/repos/fruits-repo</code> folder.</p>
</li>
</ul>
<p><code>go generate</code> is a command that will run any directives in a go file that is prefixed with <code>//go:generate</code>.</p>
<p>The generated file uses the package <code>github.com/stretchr/testify/mock</code> which is a mocking library. We need to install it by running <code>go get github.com/stretchr/testify/mock</code>.</p>
<h3 id="heading-unit-tests">Unit Tests</h3>
<p>Test files are named <code>*_test.go</code> and are placed in the same directory as the code they are testing.</p>
<h4 id="heading-basic-test">Basic Test</h4>
<p>Create a file called <code>web/fruits-api/fruits_api_test.go</code> and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> fruitsapi

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"net/http/httptest"</span>
    <span class="hljs-string">"testing"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro"</span>
    fruitsrepo <span class="hljs-string">"github.com/fluxynet/httpintro/mocks/repos/fruits-repo"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
    <span class="hljs-string">"github.com/stretchr/testify/assert"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestGet</span><span class="hljs-params">(t *testing.T)</span></span> {
    <span class="hljs-keyword">var</span> repo fruitsrepo.FruitRepository
    repo.On(<span class="hljs-string">"Get"</span>, <span class="hljs-number">1</span>).Return(&amp;httpintro.Fruit{
        ID:    <span class="hljs-number">1</span>,
        Name:  <span class="hljs-string">"apple"</span>,
        Color: <span class="hljs-string">"red"</span>,
    }, <span class="hljs-literal">nil</span>)

    api := New(&amp;repo)

    r := httptest.NewRequest(<span class="hljs-string">"GET"</span>, <span class="hljs-string">"/fruits/1"</span>, <span class="hljs-literal">nil</span>)
    w := httptest.NewRecorder()

    router := chi.NewRouter()
    router.Route(<span class="hljs-string">"/fruits"</span>, api.Route)

    router.ServeHTTP(w, r)

    assert.Equal(t, http.StatusOK, w.Code)
    assert.JSONEq(t, <span class="hljs-string">`{"id": 1, "name": "apple", "color": "red"}`</span>, w.Body.String())
    repo.AssertExpectations(t)
}
</code></pre>
<p>We can run the tests by running <code>go test ./...</code>.</p>
<p>We can also run the tests with coverage by running <code>go test ./... -coverprofile=coverage.out</code> and then <code>go tool cover -html=coverage.out</code>.</p>
<h4 id="heading-table-driven-tests">Table Driven Tests</h4>
<p>Table driven tests are useful when we want to test a function with different inputs. We can easily add more test cases without having to write more code.</p>
<p>Let us rewrite the above test using table driven tests.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> fruitsapi

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"errors"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"net/http/httptest"</span>
    <span class="hljs-string">"testing"</span>

    <span class="hljs-string">"github.com/fluxynet/httpintro"</span>
    fruitsrepo <span class="hljs-string">"github.com/fluxynet/httpintro/mocks/repos/fruits-repo"</span>
    <span class="hljs-string">"github.com/go-chi/chi/v5"</span>
    <span class="hljs-string">"github.com/stretchr/testify/assert"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestGet</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name           <span class="hljs-keyword">string</span>
        id             <span class="hljs-keyword">int</span>
        expectedStatus <span class="hljs-keyword">int</span>
        expectedBody   <span class="hljs-keyword">string</span>
        mock           <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(repo *fruitsrepo.FruitRepository)</span></span>
    }{
        {
            name:           <span class="hljs-string">"success"</span>,
            id:             <span class="hljs-number">1</span>,
            expectedStatus: http.StatusOK,
            expectedBody:   <span class="hljs-string">`{"id": 1, "name": "apple", "color": "red"}`</span>,
            mock: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(repo *fruitsrepo.FruitRepository)</span></span> {
                repo.On(<span class="hljs-string">"Get"</span>, <span class="hljs-number">1</span>).Return(&amp;httpintro.Fruit{
                    ID:    <span class="hljs-number">1</span>,
                    Name:  <span class="hljs-string">"apple"</span>,
                    Color: <span class="hljs-string">"red"</span>,
                }, <span class="hljs-literal">nil</span>)
            },
        },
        {
            name:           <span class="hljs-string">"not found"</span>,
            id:             <span class="hljs-number">1</span>,
            expectedStatus: http.StatusNotFound,
            expectedBody:   <span class="hljs-string">`{"error": "not found"}`</span>,
            mock: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(repo *fruitsrepo.FruitRepository)</span></span> {
                repo.On(<span class="hljs-string">"Get"</span>, <span class="hljs-number">1</span>).Return(<span class="hljs-literal">nil</span>, httpintro.ErrNotFound)
            },
        },
        {
            name:           <span class="hljs-string">"internal server error"</span>,
            id:             <span class="hljs-number">1</span>,
            expectedStatus: http.StatusInternalServerError,
            expectedBody:   <span class="hljs-string">`{"error": "internal server error"}`</span>,
            mock: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(repo *fruitsrepo.FruitRepository)</span></span> {
                repo.On(<span class="hljs-string">"Get"</span>, <span class="hljs-number">1</span>).Return(<span class="hljs-literal">nil</span>, errors.New(<span class="hljs-string">"internal server error"</span>))
            },
        },
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            <span class="hljs-keyword">var</span> repo fruitsrepo.FruitRepository
            tt.mock(&amp;repo)

            api := New(&amp;repo)

            r := httptest.NewRequest(<span class="hljs-string">"GET"</span>, <span class="hljs-string">"/fruits/1"</span>, <span class="hljs-literal">nil</span>)
            w := httptest.NewRecorder()

            router := chi.NewRouter()
            router.Route(<span class="hljs-string">"/fruits"</span>, api.Route)

            router.ServeHTTP(w, r)

            assert.Equal(t, tt.expectedStatus, w.Code)

            <span class="hljs-keyword">if</span> tt.expectedBody == <span class="hljs-string">""</span> {
                assert.Equal(t, tt.expectedBody, w.Body.String())
            } <span class="hljs-keyword">else</span> {
                assert.JSONEq(t, tt.expectedBody, w.Body.String())
            }

            repo.AssertExpectations(t)
        })
    }
}
</code></pre>
<h2 id="heading-the-path-forward">The path forward</h2>
<p>We have covered the basics of http, sql and testing in go.</p>
<p>For the sake of simplicity and clarity, we have stuck with the standard library and a few third party packages. There are many more packages that can be used to make our lives easier, for example:</p>
<ul>
<li><p>(Cobra)[https://github.com/spf13/cobra] - A library for creating command line applications</p>
</li>
<li><p>(Gorm)[https://gorm.io/] - An ORM for SQL databases</p>
</li>
</ul>
<p>There is still a lot to learn and we have only scratched the surface.</p>
<p>Here are some topics you can look into next:</p>
<ul>
<li><p><a target="_blank" href="https://blog.golang.org/context">Context</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/organizing-go-code">Code Organization</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/using-go-modules">Go Modules</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/pipelines">Go Channels</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/concurrency-is-not-parallelism">Go Concurrency</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/go1.13-errors">Go Errors</a></p>
</li>
<li><p><a target="_blank" href="https://blog.golang.org/generics-next-step">Go Generics</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>