A simple guide to build a web app with Go

This article aims to be a guide for building a web application completely in Go. I think the stack (introduced in the next section) is pleasant to work with and a breath of fresh air if you are coming from a JS background. We will be building a small "Hello World"-application, so it can be used a starting point for more complex applications.

The Stack

The main libraries we will be using are:

Building the App

Creating a Web Server with Fuego

Fuego is a Go web framework that is relatively new. It is based on the new Go 1.22 router and supports OpenAPI generation from code out of the box. It is also compatible with net/http from the standard library, so that every middleware or handler for net/http can be used with Fuego.

Some say the standard library is sufficient, but personally I like the structured approach of a framework a bit more. Another reason is the centralized error handling and the nice integration with Templ. So let´s get started and create a simple web server. The following is an example from the official documentation.

package main

import "github.com/go-fuego/fuego"

func main() {
	s := fuego.NewServer()

	fuego.Get(s, "/", func(c fuego.ContextNoBody) (string, error) {
		return "Hello, World!", nil
	})

	s.Run()
}

As you can see, Fuego offers a Get method for registering a route which accepts GET requests. Analogously, there exist methods for all other HTTP operations like POST and DELETE. These routing methods always expect the server instance, the path and a function, also called controller. Controllers handle the requests and responses and can come in different types depending on what you expect and return. They always expect a context (either with a body or without one) and return a value and an error. For example, the controller above simply returns a string and does not expect any body.

Now we want to return some more complex HTML and not just a string. For creating such HTML one could use html/template from the Go standard library. However, in this guide we will be using Templ. Templ allows us to write Go Code inside a template and make use of Go´s type safety. This has the advantage that you notice problems with your templates at compile time and not at runtime, which is especially handy for larger applications.

Composing the Frontend with Templ

First, we need to install Templ, so we can compile our Templ components to Go code.

Be sure to add the installed Templ binary to your path, so you can access it later from all locations. To add the dependency to your project, run

At this point we are ready to create our first Templ component. Let´s skip the simple "Hello World" component and assume we want to build a site with a common element like a navigation. This is a little bit more interesting and shows the composition feature of Templ in action. Have a look at the following code:

import "appname/components"

templ Base(title string) {
  <!DOCTYPE html>
  <html>
    <head>
      <link rel="stylesheet" href="/static/css/styles.css" />
      <title>{title}</title>
    </head>
    <body>
      @components.Header()
      <main class="container">
          { children... }
      </main>
    </body>
  </html>
}

It demonstrates a few interesting things

Now, we can use the base layout in all our pages, so the header stays on every page. In fact, the homepage of this very site is a Templ component:

templ Home() {
  @Base("Nik´s HQ") {
    <article>
      <h2>Hello, friend.</h2>
      <section>
        My name is Niklas and I am a software engineer from Germany.
        <br/>
        This is the place where I share my thoughts on different topics, which are mainly tech
        and programming related.
      </section>
      <blockquote>
        “Only the disciplined are free in life.”
        <footer>
          <cite>— Eliud Kipchoge</cite>
        </footer>
      </blockquote>
    </article>
  }
}

The title is "Nik´s HQ" and everything between the curly braces represents the children and will be put inside the main tag.

As the last step we need to compile our templates to Go code to get the type safety. For that, run the command templ generate in the root directory of your project. This will create a *_templ.go file for all of your *.templ files.

Connect Fuego with Templ

Currently, we have a simple Fuego web server and a pair of Templ components, so the next step is to put them together. Fuego comes with built-in support for Templ components. To render one, we can register a new route with a controller that returns the component.

fuego.Get(s, "/home", func(c fuego.ContextNoBody) (fuego.Templ, error) {    
    return Home(), nil
})

If our Templ component sits in the same Go package like our server, we can just call the component via its name. And that´s it, navigating to /home will render our Home component. Summarized, the workflow from now on is to write a template, generate the go code, build the app and repeat.

Styling and static files

Every app needs some styling but for many people (mostly backend developers) writing CSS is also the most boring task. For that reason I especially like Pico, which brings in some nice defaults and is very minimalistic. To bring it in, we download the minified css of pico from their website and add the following line to our base layout:

<link rel="stylesheet" href="/static/css/pico.min.css" />

We are referring to a /static folder, which isn´t yet handled by our web server. Therefore, we need to add a handler for all the static content, that should be served. Go has a package in its standard library called embed, which allows you to embed files into the Go program itself. The nice thing is that the end result is still a single executable, so you can share and deploy it as easy as without the static files. I like to have an embed.go file inside the static folder, which contains the handler:

//go:embed *
var static embed.FS

// StaticHandler returns a http.Handler that will serve files from
// the given file system.
func StaticHandler() http.Handler {
	return http.FileServer(http.FS(static))
}

Then we can add the route to the Fuego server:

// register route for static files
fuego.Handle(server, "/static/", http.StripPrefix("/static", static.StaticHandler()))

The StripPrefix() method is needed because our handler sits inside the static folder, which would lead to a /static/static path. At this point, the CSS should be loaded and applied to our templates.

Adding interactivity

To make it a well-rounded app and not just a static site, we can add some interactivity. In recent years htmx gained more and more popularity. Htmx allows you to make AJAX calls directly from HTML without using JavaScript. For not so interaction-heavy apps, this is completely adequate and most of the time you just don´t need React or any other JavaScript Framework. It is also very easy to install, you only need to add a single script tag in the header, and you are done.

<script src="/static/htmx.min.js"></script>

Possible Project Structure

Sooner or later the app will grow and often the question after the best project structure arises. My advice is to take the last section of this official Go blog post as an orientation. It is a really simple structure, which does not overcomplicate things.

Also, the example project of Fuego can be a nice starting point. It follows a more structured MVC-like approach, which can help with bigger apps.

Next Steps

In the end, here are some thoughts on what could possibly be added:

This concludes my guide and I hope you can get something out of it.

Bye for now :)

Niklas


Published on: 18. May 2024

Last edit: 22. August 2024

Back