This post explains a minimal HTTP server written in pure Go, using only the standard library. The goal is to demonstrate how to implement middleware-like without using any third party library like Gin, Echo, or Chi.

High-Level Architecture

  • A static backend registry (backendList)
  • A route lookup function
  • A custom middleware
  • Plain http.HandlerFunc handlers
  • The standard net/http multiplexer

No reflection, no dependency injection, no magic.

Backend Definition

type Backend struct {
	Path           string
	isAuthRequired bool
	Handle         http.HandlerFunc
}

Each backend explicitly declares:

  • Path: the HTTP path
  • isAuthRequired: whether authentication is enforced
  • Handle: the actual request handler

This structure replaces framework routing tables and keeps behavior explicit.

Backend Registry

var backendList []Backend = []Backend{
	{Path: "/", isAuthRequired: false, Handle: HandleHome},
	{Path: "/admin", isAuthRequired: true, Handle: HandleAdmin},
}

This is a static routing table.

Key properties:

  • Routes are declared once
  • Authentication rules are colocated with the route
  • No runtime mutation
  • Easy to reason about during code review

Route Lookup Logic

func backendByPath(path string) *Backend {
	for i := range backendList {
		if backendList[i].Path == path {
			return &backendList[i]
		}
	}
	return nil
}

This performs a linear scan to find the backend for the current path.

Middleware Implementation

func middleware(handle http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

This is classic Go middleware, it wraps another http.HandlerFunc and it intercepts the request before passing control downstream

Route Validation

backendNow := backendByPath(r.URL.Path)

if backendNow == nil {
	w.WriteHeader(404)
	fmt.Fprintf(w, `{"message": "Not Found"}`)
	return
}

Even though the handler is registered, the middleware still validates the route.

This allows centralized control and consistent error responses.

Validating Authentication (api-key)

if backendNow.isAuthRequired {
	auth := r.Header.Get("api-key")
	if auth != "secret123" {
		w.WriteHeader(401)
		fmt.Fprintf(w, `{"message": "Unauthorized"}`)
		return
	}
}

Authentication is:

  • Header-based
  • Stateless
  • Route-specific

This mimics what real API gateways do, but without JWTs or OAuth complexity.

Request Timing and Logging

start := time.Now()
w.Header().Set("Content-Type", "application/json")
ending := time.Since(start)

log.Printf(`request to "%s" executed in "%s".`, r.URL.Path, &ending)

This section demonstrates:

  • Request lifecycle instrumentation
  • Centralized logging
  • Header normalization

HTTP Handlers

Home Handler

func HandleHome(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(201)
	fmt.Fprintf(w, `{"message": "Hello, World to Home!"}`)
}

Simple response, no method branching, no authentication.

Admin Handler

func HandleAdmin(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.WriteHeader(200)
		fmt.Fprintf(w, `{"message": "Hello, World to Admin"}`)
	case "POST":
		w.WriteHeader(201)
		fmt.Fprintf(w, `{"message": "Create the World to Admin"}`)
	default:
		w.WriteHeader(405)
		fmt.Fprintf(w, `{"message": "Method Not Allowed"}`)
	}
}

Key points:

  • Explicit method handling
  • No helper functions (educational purposes)
  • Full control over HTTP semantics
  • Of course you should validate body (educational purposes)

Server Bootstrap

func main() {
	for _, backend := range backendList {
		log.Printf(
			"Registering backend auth required: %5t, at path: %s",
			backend.isAuthRequired,
			backend.Path,
		)
		http.HandleFunc(backend.Path, middleware(backend.Handle))
	}

	log.Println("Starting server on :8080")
	http.ListenAndServe(":8080", nil)
}

This loop dynamically registers:

  • Each backend
  • Wrapped with the middleware
  • On the default http.ServeMux

This is effectively a poor man’s router + middleware chain, built using only the standard library.

Why This Design Works

Advantages:

  • Zero dependencies
  • Transparent control flow
  • Easy to debug
  • Excellent for learning HTTP internals

Limitations:

  • No path parameters
  • Linear route lookup
  • Manual error handling
  • No middleware chaining

Again, this is just for educational purposes in how to fastly execute things in Go. That’s the real value here.

Full Code

package main

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

type Backend struct {
	Path           string
	isAuthRequired bool
	Handle         http.HandlerFunc
}

var backendList []Backend = []Backend{
	{Path: "/", isAuthRequired: false, Handle: HandleHome},
	{Path: "/admin", isAuthRequired: true, Handle: HandleAdmin},
}

func backendByPath(path string) *Backend {
	for i := range backendList {
		if backendList[i].Path == path {
			return &backendList[i]
		}
	}
	return nil
}

func middleware(handle http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		backendNow := backendByPath(r.URL.Path)

		if backendNow == nil {
			w.WriteHeader(404)
			fmt.Fprintf(w, `{"message": "Not Found"}`)
			return
		}

		if backendNow.isAuthRequired {
			auth := r.Header.Get("api-key")
			if auth != "secret123" {
				w.WriteHeader(401)
				fmt.Fprintf(w, `{"message": "Unauthorized"}`)
				return
			}
		}

		start := time.Now()
		w.Header().Set("Content-Type", "application/json")
		ending := time.Since(start)

		log.Printf(`request to "%s" executed in "%s".`, r.URL.Path, &ending)

		handle.ServeHTTP(w, r)
	})
}

func HandleHome(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(201)
	fmt.Fprintf(w, `{"message": "Hello, World to Home!"}`)
}

func HandleAdmin(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, `{"message": "Hello, World to Admin"}`)
		w.WriteHeader(200)
		return
	case "POST":
		fmt.Fprintf(w, `{"message": "Create the World to Admin"}`)
		w.WriteHeader(201)
		return
	default:
		fmt.Fprintf(w, `{"message": "Method Not Allowed"}`)
		w.WriteHeader(405)
		return
	}
}

func main() {
	for _, backend := range backendList {
		log.Printf("Registering backend auth required: %5t, at path: %s", backend.isAuthRequired, backend.Path)
		http.HandleFunc(backend.Path, middleware(backend.Handle))
	}
	log.Println("Starting server on :8080")
	http.ListenAndServe(":8080", nil)
}