GOTTH for people in a rush

Beginner: Starting a Go server

In this tutorial, we’ll walk through multiple ways to create and run a basic Go web server using the net/http package. These approaches will form the foundation for serving HTML templates, APIs, and eventually integrating with Tailwind CSS and htmx in the GOTTH stack.

1. Minimal Server

Here’s the simplest way to serve a single “Hello, world!” response on port 8080:

// minimal.go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, world!")
    })

    http.ListenAndServe(":8080", nil)
}

Simply run go run minimal.go, then navigate to http://localhost:8080.

2. Using a Named Handler Function

If you’d like more clarity, you can define a separate function for handling requests and attach that to a route:

// namedhandler.go
package main

import (
    "fmt"
    "net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello from a named handler!")
}

func main() {
    http.HandleFunc("/", helloHandler)
    http.ListenAndServe(":8080", nil)
}

This approach is more flexible since you can define multiple handler functions for different endpoints or for more sophisticated request handling.

3. Multiple Routes with the Default Mux

Go’s default multiplexer (often just called the “default mux”) can handle multiple routes by calling http.HandleFunc more than once:

// multipleroutes.go
package main

import (
    "fmt"
    "net/http"
)

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Welcome to the Home Page!")
}

func aboutHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Learn about us here.")
}

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/about", aboutHandler)

    http.ListenAndServe(":8080", nil)
}

Now you can visit http://localhost:8080/ and http://localhost:8080/about to see different responses.

4. Using a Custom Mux

If you prefer not to rely on Go’s default mux, you can create your own using http.NewServeMux. This is helpful for organizing routes in larger applications:

// custommux.go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "This is the root path, served by a custom mux.")
    })

    mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello from a custom mux route!")
    })

    http.ListenAndServe(":8080", mux)
}

Either approach (default mux vs. custom mux) works fine. It’s largely a matter of personal preference or project organization.