ub is a minimal bootstrapper for
go.uber.org/fxthat handles the application lifecycle for you.
go.uber.org/fx is a powerful dependency injection framework for Go. However, wiring up a long-running application (like a web server or a message queue consumer) into its lifecycle can be verbose. You typically need to manage fx.Lifecycle hooks, run your main logic in a separate goroutine to avoid blocking startup, and manually handle shutdown signals.
ub abstracts away all of this boilerplate. It provides a single function, ub.Run, that lets you focus on your application's core logic, while ub handles the rest.
- Zero Boilerplate: Run your long-running service with a single function call.
- Full
fxIntegration: Built on top offx, so you can use all of its features like dependency injection, modules, and more. - Graceful Shutdown: Automatically handles OS signals (e.g.,
Ctrl+C) and ensures your application shuts down gracefully. - Dependency Injection: Your main function's parameters are automatically supplied by the
fxcontainer.
go get github.com/lftk/ubHere is a complete example of a simple HTTP server running with ub.
package main
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/lftk/ub"
"go.uber.org/fx"
)
func main() {
opts := []fx.Option{
fx.NopLogger,
// Provide the http.Handler dependency to the Fx container.
fx.Provide(NewHandler),
}
// Run the application with our serve function.
ub.Run(serve, opts...)
}
// serve is our main application logic.
// It receives the application context and any dependencies from Fx.
func serve(ctx context.Context, h http.Handler) error {
// Create a new HTTP server.
s := &http.Server{
Addr: "127.0.0.1:8080",
Handler: h,
}
// Register a function to be called when the context is cancelled.
// This is the key to graceful shutdown.
context.AfterFunc(ctx, func() {
// The context is cancelled, so we begin shutting down the server.
// We use a background context for the shutdown process itself.
_ = s.Shutdown(context.Background())
})
fmt.Println("HTTP server listening on", s.Addr)
// Start the server. This is a blocking call.
err := s.ListenAndServe()
// ListenAndServe always returns an error. If it's ErrServerClosed,
// it means the shutdown was graceful, so we treat it as success.
if errors.Is(err, http.ErrServerClosed) {
err = nil
}
return err
}
// NewHandler creates a new http.Handler.
// Fx will automatically call this function and provide its result
// to any other function that needs an http.Handler.
func NewHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Got a request.")
fmt.Fprintln(w, "Hello, world!")
})
return mux
}You provide ub.Run with your main function (serve in the example above).
- The first argument of your function must be
context.Context.ubwill use this context to signal a graceful shutdown. - Any other arguments are treated as dependencies that
fxwill provide. ubtransparently creates anfx.Lifecyclehook that runs your function in the background.- When your function returns, or when an OS signal is received,
ubensures the entirefxapplication shuts down gracefully.
MIT