---
title: "Learning Go: Day Thirteen"
date: 2024-05-15T08:00:00.0Z
tags:
    - learning
    - go
excerpt: "Creating some basic HTML output, and bundling static files"
---

So now I want to start working on putting together a basic interface. This post won't be about creating the interface itself, but instead just the ground work that will allow me to build it over the next post or two.

## Creating some HTML output

Alright, this was actually really straightforward. There's a module called [html/template](https://pkg.go.dev/html/template) that can be used to parse HTML, and includes a templating language.

So I've created a super-simple HTML template at `templates/index.html`, and then use the `template.ParseFiles` function to parse and display it:

```html
<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Oopsie</title>
    </head>
    <body>
        <h1>Oopsie uptime monitoring</h1>
    </body>
</html>
```

```go
// main.go

http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
    pings := ping.ListGroupedBySite(db)

    if _, err := json.Marshal(pings); err != nil {
        w.Write([]byte(err.Error()))
        w.WriteHeader(http.StatusInternalServerError)
        return
    } else {
        w.Header().Set("Content-Type", "text/html")

        t, _ := template.ParseFiles("templates/index.html")
        t.Execute(w, nil)
    }
})
```

This works! But I can't deploy it, because when I compile the binary and upload it to my server it can't find the template files. Luckily, there's a package that can help me do that.

## Bundling static files

This part is basically magic. I have no idea how it works, but there is a package called [embed](https://pkg.go.dev/embed). I can use it to create an instance of `FS` with some bundled static content, and that then gets bundled into my binary, and it takes about 3 lines of code to do it 🤯.

```go
// main.go

import (
    "embed"
)

//go:embed templates/*
var content embed.FS

http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
    pings := ping.ListGroupedBySite(db)

    if _, err := json.Marshal(pings); err != nil {
        w.Write([]byte(err.Error()))
        w.WriteHeader(http.StatusInternalServerError)
        return
    } else {
        w.Header().Set("Content-Type", "text/html")

        t, _ := template.ParseFS(content, "templates/index.html")
        t.Execute(w, nil)
    }
})
```

And then when I run `go build`, the resulting binary file has the HTML bundled in it and I can run it on my server. This is a _really_ powerful thing to have built directly into the standard library.

So that's it for today, like I mentioned earlier, my next one or two posts will mostly be focused on making a UI.