Creating an HTTP server in Go

June 18, 2024

Introduction

This tutorial would show you how to create a basic http server in Go lang. For this we’ll need Go 1.22 as it offers built-in enhanced routing that would save us time in thinking what framework should we use.

Folder Structure

  • go.mod
  • go.work
  • main.go
  • handlers/
    • home.go
    • articles.go

Initilizing Project

Create project.

$ mkdir http-server; cd http-server;

Initialize project.

$ go mod init http-server;
$ go work init .; # enable workspaces

Creating the router

NewServeMux would return a ServeMux that would serve us our router for the application. Let’s create barebone http server first to try it out.

package main

import (
	"fmt"
	"log"
	"net/http"
)

func HomeRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello World!")
}

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

	mux.HandleFunc("/{$}", HomeRoute) // <--- `/{$}` only matches `/`

	err := http.ListenAndServe(":3000", mux)

	if err != nil {
		log.Fatalln("Cannot run the server", err)
	}
}

Method Specific Route

To filter specific request method to a route, we could use a prefix to identify the target request method.

- mux.HandleFunc("/{$}", HomeRoute)
+ mux.HandleFunc("GET /{$}", HomeRoute)

Learn more about ServeMux patterns

Testing the application…

GET method has response

$ curl localhost:3000
Hello World!

POST method throws an error

$ curl -XPOST localhost:3000
Method Not Allowed

Getting Path Value

One of the biggest game changer introduced in Go 1.22 is the ability to read url paths.

func ArticlesRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Reading article", r.PathValue("article"))
}

Register the path:

mux.HandleFunc("/articles/{article}", ArticlesRoute)

What do we have now?

package main

import (
	"fmt"
	"log"
	"net/http"
)

func HomeRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello World!")
}

+ func ArticlesRoute(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprintln(w, "Reading article", r.PathValue("article"))
+ }

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

	mux.HandleFunc("GET /{$}", HomeRoute)
+	mux.HandleFunc("/articles/{article}", ArticlesRoute)

	err := http.ListenAndServe(":3000", mux)

	if err != nil {
		log.Fatalln("Cannot run the server", err)
	}
}

Testing the new route

$ curl localhost:3000/articles/hello-world
Reading article hello-world

Organizing our project

To better organize our code, we’ll create a new module.

mkdir handlers;

Then move our route to each separate module inside handlers

// ./handlers/home.go
package handlers

import (
	"fmt"
	"net/http"
)

func HomeRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello World!")
}
// ./handlers/articles.go
package handlers

import (
	"fmt"
	"net/http"
)

func ArticlesRoute(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Reading article", r.PathValue("article"))
}

Update our main.go to read the handlers submodule.

package main

import (
	"http-server/handlers"
	"log"
	"net/http"
)

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

	mux.HandleFunc("GET /{$}", handlers.HomeRoute)
	mux.HandleFunc("/articles/{article}", handlers.ArticlesRoute)

	err := http.ListenAndServe(":3000", mux)

	if err != nil {
		log.Fatalln("Cannot run the server", err)
	}
}

Conclusion

Here we have learned about how to create a basic HTTP server using Go and how to utilize. Hoping that this tutorial helped kickstart your dream project in Go.

To see the full project in this tutorial click here.