Skip to content

Register a handler

We built a server that can receive requests, but it doesn't yet know how to handle them. Let's fix that.

  1. Define a basic HTTP handler that copies the incoming request body to the response. Add the following to the bottom of your file.

    // EchoHandler is an http.Handler that copies its request body
    // back to the response.
    type EchoHandler struct{}
    
    // NewEchoHandler builds a new EchoHandler.
    func NewEchoHandler() *EchoHandler {
       return &EchoHandler{}
    }
    
    // ServeHTTP handles an HTTP request to the /echo endpoint.
    func (*EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
       if _, err := io.Copy(w, r.Body); err != nil {
           fmt.Fprintln(os.Stderr, "Failed to handle request:", err)
       }
    }
    

    Provide this to the application.

           fx.Provide(
               NewHTTPServer,
               NewEchoHandler,
           ),
           fx.Invoke(func(*http.Server) {}),
    
  2. Next, write a function that builds an *http.ServeMux. The *http.ServeMux will route requests received by the server to different handlers. To begin with, it will route requests sent to /echo to *EchoHandler, so its constructor should accept *EchoHandler as an argument.

    // NewServeMux builds a ServeMux that will route requests
    // to the given EchoHandler.
    func NewServeMux(echo *EchoHandler) *http.ServeMux {
       mux := http.NewServeMux()
       mux.Handle("/echo", echo)
       return mux
    }
    

    Likewise, provide this to the application.

           fx.Provide(
               NewHTTPServer,
               NewServeMux,
               NewEchoHandler,
           ),
    

    Note that NewServeMux was added above NewEchoHandler--the order in which constructors are given to fx.Provide does not matter.

  3. Lastly, modify the NewHTTPServer function to connect the server to this *ServeMux.

    func NewHTTPServer(lc fx.Lifecycle, mux *http.ServeMux) *http.Server {
       srv := &http.Server{Addr: ":8080", Handler: mux}
       lc.Append(fx.Hook{
    
  4. Run the server.

    [Fx] PROVIDE    *http.Server <= main.NewHTTPServer()
    [Fx] PROVIDE    *http.ServeMux <= main.NewServeMux()
    [Fx] PROVIDE    *main.EchoHandler <= main.NewEchoHandler()
    [Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
    [Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
    [Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
    [Fx] INVOKE             main.main.func1()
    [Fx] HOOK OnStart               main.NewHTTPServer.func1() executing (caller: main.NewHTTPServer)
    Starting HTTP server at :8080
    [Fx] HOOK OnStart               main.NewHTTPServer.func1() called by main.NewHTTPServer ran successfully in 7.459µs
    [Fx] RUNNING
    
  5. Send a request to the server.

    $ curl -X POST -d 'hello' http://localhost:8080/echo
    hello
    

What did we just do?

We added more components with fx.Provide. These components declared dependencies on each other by adding parameters to their constructors. Fx will resolve component dependencies by parameters and return values of the provided functions.