JSTAcademy
0 XP
Dashboard
Crash Courses
HTTP Server with net/http
45 min
PhD+195 XP
Crash Courses · PhD

HTTP Server with net/http

Serve HTTP requests, handle routes, and write JSON responses
45 min read+195 XP on completionCert: Go Crash Course
Tap any word in the text below to start reading from there.

HTTP Server with net/http

The Simplest Server

package main

import (
    "fmt"
    "net/http"
)

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

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

Visit http://localhost:8080 — you should see "Hello, Web!".

Returning JSON

package main

import (
    "encoding/json"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func getUser(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    user := User{ID: 1, Name: "Jordan"}
    json.NewEncoder(w).Encode(user)
}

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

Status Codes

func notFound(w http.ResponseWriter, r *http.Request) {
    http.Error(w, "Not Found", http.StatusNotFound) // 404
}

Custom ServeMux

Using the global DefaultServeMux is fine for simple programs, but in production you want your own:

mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/api/users", usersHandler)

server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()

Setting timeouts is critical — the default http.Server has no timeouts.

Reading Request Body

func createUser(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
        return
    }

    var user User
    err := json.NewDecoder(r.Body).Decode(&user)
    if err != nil {
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }

    // process user...
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

Reading URL Query Parameters

func search(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query().Get("q")
    fmt.Fprintf(w, "Searching for: %s", query)
}
// GET /search?q=golang

Path Variables (Go 1.22+)

Go 1.22 added pattern matching to the standard mux:

mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "User ID: %s", id)
})
0%