Problem: You want to create a simple web application that responds to an HTTP request and sends back an HTTP response.
Solution: Use the net/http package to create a simple Hello World web application.
You want to create a simple web application running on port 8000 that shows a Hello World message when a user visits the / URL.
The only package you need is the net/http package:
package main import ( "net/http" ) func main() { http.HandleFunc("/", index) http.ListenAndServe(":8000", nil) } func index(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) }
Starting from the second line in the main function: here, you call the http.ListenAndServe function to start the web server. The first parameter is the address to listen on, which is in the format <host>:<port> . If the host is provided, the listener will listen only to that IP address. If it’s left empty, as in this case, for example, :8000 , it will listen on all available unicast and anycast IP addresses of the local system.
The second parameter is a handler; that is, it is a struct that implements the http.Handler interface. The http.Handler interface has only one method, ServeHTTP , which takes in an http.ResponseWriter and an http.Request as parameters. The http.ResponseWriter is used to write the response back to the client, and the http.Request contains all the information about the request.
You might be wondering why you are setting up the server to use only one handler; you would expect a lot more than just one handler. This is because in Go, the multiplexer, the router that matches the request URI to a handler, is a handler itself!
In this case, it’s left as nil, which means it will assume the default multiplexer, http.DefaultServeMux . http.DefaultServeMux is an instance of the http.ServeMux struct, which in turn, implements the http.Handler interface. You don’t normally interact directly with http.DefaultServeMux , despite it being a variable, but many of the functions in the package are nothing more than wrappers around it, as you will see in a while.
The http.ServeMux multiplexer has a HandleFunc method that registers a function as a handler for a given URL pattern. This HandleFunc method takes in a URL pattern and a function as parameters. The function must have the signature func(http.ResponseWriter, *http.Request) and will be called when a request matches the URL pattern.
Now when you look at the first line of the main function, things should become clear. You call the http.HandleFunc function to register the index function as the handler for the / URL pattern. Internally what it does is simply call the HandleFunc method of the http.DefaultServeMux multiplexer.
The index function is quite trivial. In this case, you simply write the string Hello World to the http.ResponseWriter . It might not look like it, but w is a pointer. http.ResponseWriter is an interface so you can’t tell if it’s a pointer or not. Whatever is written to the http.ResponseWriter will then be sent back to the client as the response body.
In this recipe, you used the multiplexer in the standard library because it is the default implementation. It is quite common to use more sophisticated and performant multiplexers offered by third-party packages; for example, the chi package .
Here’s how the code would look with a third-party multiplexer like chi:
package main import ( "net/http" "github.com/go-chi/chi/v5" ) func main() { mux := chi.NewRouter() mux.Get("/", index) http.ListenAndServe(":3000", mux) } func index(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) }
As you can see, the main change is that you need to create a new multiplexer, mux , and this will be used instead of the default multiplexer when you start the server with http.ListenAndServe . Third-party multiplexers are more optimal and capable. In most cases, you should use one unless you are writing something that only uses the standard library.