Go Tutorial · Standard Library · Lesson 28 of 30

Go HTTP Server

Build a small JSON API with net/http — no framework needed.

A JSON API in the standard library

Go's net/http is production-grade. Since Go 1.22, routes can include methods and path parameters:

package main

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

type Todo struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}

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

	mux.HandleFunc("GET /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
		todo := Todo{ID: r.PathValue("id"), Title: "Learn Go"}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(todo)
	})

	mux.HandleFunc("POST /todos", func(w http.ResponseWriter, r *http.Request) {
		var t Todo
		if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
			http.Error(w, "invalid JSON", http.StatusBadRequest)
			return
		}
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(t)
	})

	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}
go run .
curl localhost:8080/todos/42

Each request runs in its own goroutine. Use r.Context() to stop work when the client disconnects, and set timeouts on an http.Server in production.

Testing handlers

net/http/httptest lets you call a handler without a network:

Example 1
package main

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

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %s!", r.PathValue("name"))
	})

	req := httptest.NewRequest("GET", "/hello/gopher", nil)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)
	fmt.Println(rec.Code, rec.Body.String())

	rec = httptest.NewRecorder()
	mux.ServeHTTP(rec, httptest.NewRequest("POST", "/hello/gopher", nil))
	fmt.Println(rec.Code)
}

Output

200 Hello, gopher!
405

Run this on your computer — see “Get Started” for setup.

Test yourself

Since Go 1.22, how do you read the {id} path parameter in a handler?

  1. r.Param("id")
  2. r.PathValue("id")
  3. mux.Vars(r)["id"]
  4. r.URL.Query().Get("id")
Show answer

Answer: r.PathValue("id")