fxapp

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2025 License: MIT Imports: 7 Imported by: 0

README

fxapp

fxapp is a Go package that provides a convenient wrapper around go.uber.org/fx to manage the lifecycle of a dependency injection-based application. It simplifies starting, running, and gracefully shutting down your application.

Requirements

  • Go >=1.22

Installation

go get github.com/SnapSuzun/go-fxapp

Configuration

The application can be configured using the fxapp.Config struct:

type Config struct {
	// StartTimeout is the maximum duration to wait for the application's services to start.
	StartTimeout time.Duration `envconfig:"APP_START_TIMEOUT" default:"10s"`
	// ShutdownTimeout is the maximum duration to wait for the application's services to shut down gracefully.
	ShutdownTimeout time.Duration `envconfig:"APP_SHUTDOWN_TIMEOUT" default:"10s"`
}

Core Components

App

The App is the core of the package. It encapsulates an fx.App instance and manages its state (stopped, starting, started).

  • NewApp(cfg Config, opts ...fx.Option): Creates a new application instance.
  • Run(ctx context.Context): Starts the application and blocks until it stops.
  • Shutdown(ctx context.Context): Initiates a graceful shutdown of the application.
  • Shutdowner: An interface that can be injected into any component to allow it to trigger a global application shutdown.
RunnerInvoke

RunnerInvoke is an fx.Option for integrating long-running processes (e.g., a web server) into the application lifecycle. It manages a component that implements the Runner interface:

type runner interface {
	Run(ctx context.Context) error
}
  • The Run method is launched in a goroutine on application start.
  • When the Run method completes, it triggers a graceful shutdown of the entire application.
  • On application stop, the context passed to Run is canceled to signal the process to terminate.
WorkerInvoke

WorkerInvoke is an fx.Option for components that manage their own lifecycle via Start/Shutdown methods and signal completion. It requires a component that implements the worker interface:

type worker interface {
	Start(ctx context.Context) error
	Shutdown(ctx context.Context) error
	Done() <-chan struct{}
	Err() error
}
  • OnStart, it calls the Start method and launches a goroutine to monitor the Done() channel.
  • OnStop, it calls the Shutdown method.
  • When the Done() is closed, it triggers a graceful application shutdown.
TaskInvoke

TaskInvoke is similar to RunnerInvoke but is designed for tasks with explicit Run and Shutdown methods. It manages a component implementing the task interface:

type task interface {
	Run(ctx context.Context) error
	Shutdown(ctx context.Context) error
}
  • The Run method is launched in a goroutine on application start.
  • OnStop, the Shutdown method is called, and the system waits for the Run method to complete.
  • When the Run method finishes, it triggers a graceful shutdown of the entire application.

Usage Example

Here is an example of a simple web server managed by fxapp:

package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/SnapSuzun/go-fxapp"
	"go.uber.org/fx"
)

// HTTPServer represents our web server.
type HTTPServer struct {
	http.Server
}

// NewHTTPServer creates a new server.
func NewHTTPServer() *HTTPServer {
	return &HTTPServer{
		Server: http.Server{
			Addr: ":8087",
			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, "Hello, world!")
			}),
		},
	}
}

// Run starts the server and conforms to the fxapp.Task interface.
func (s *HTTPServer) Run(context.Context) error {
	log.Println("Starting HTTP server on", s.Addr)
	err := s.ListenAndServe()
	if errors.Is(err, http.ErrServerClosed) {
		err = nil
	} else if err != nil {
		log.Printf("ListenAndServe failed: %v", err)
	}

	return err
}

func main() {
	// Create a new app with default config.
	app := fxapp.NewApp(fxapp.Config{
		StartTimeout:    10 * time.Second,
		ShutdownTimeout: 10 * time.Second,
	})

	// Provide the server to the DI container and use RunnerInvoke
	// to manage its lifecycle.
	app.WithOpts(
		fx.Provide(NewHTTPServer),
		fxapp.TaskInvoke[*HTTPServer](),
	)

	// Run the application until it stops.
	if err := app.Run(context.Background()); err != nil {
		log.Fatalf("App failed: %v", err)
	}

	log.Println("App stopped gracefully.")
}

License

The MIT License (MIT)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrAppAlreadyStarted = errors.New("app already started")

Functions

func RunnerInvoke

func RunnerInvoke[T runner]() fx.Option

RunnerInvoke creates a fx.Option that integrates a long-running process into the application's lifecycle. It accepts a generic type T that must implement the runner interface (a Run(ctx) method). OnStart, it launches the Run method in a separate goroutine. OnStop, it cancels the context passed to the Run method and waits for it to finish. When the Run method completes, it triggers a graceful shutdown of the entire application.

func TaskInvoke

func TaskInvoke[T task]() fx.Option

TaskInvoke creates a fx.Option that integrates a task into the application's lifecycle. It accepts a generic type T that must implement the task interface (Run/Shutdown methods). OnStart, it launches the Run method in a separate goroutine. OnStop, it calls the Shutdown method and waits for the Run method to complete. When the Run method completes, it triggers a graceful shutdown of the entire application.

func WorkerInvoke

func WorkerInvoke[T worker]() fx.Option

WorkerInvoke creates a fx.Option that integrates a worker process into the application's lifecycle. It accepts a generic type T that must implement the worker interface (Start/Shutdown/Done/Err methods). OnStart, it launches the Start method and then monitors Done() in a separate goroutine. OnStop, it calls Shutdown method of the worker. If Err() reports non-nil error when Done() closes, it triggers a graceful shutdown of the entire application.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App is an application wrapper that manages the lifecycle of a fx-based application. It handles starting, running, and gracefully shutting down the application. The application can be in one of three states: stopped, starting, or started.

func NewApp

func NewApp(cfg Config, opts ...fx.Option) *App

NewApp creates a new application instance with the given configuration.

func (*App) Done

func (app *App) Done() <-chan struct{}

Done returns a channel that is closed after the application has fully stopped (all fx stop hooks have completed).

func (*App) Err

func (app *App) Err() error

Err returns the first non-nil error that caused or occurred during shutdown. It is nil if the application stopped cleanly.

func (*App) InitFxApp

func (app *App) InitFxApp() *fx.App

InitFxApp constructs (but does not start) a new fx.App using the accumulated options. This is primarily useful for tests or advanced customization.

func (*App) Run

func (app *App) Run(ctx context.Context) (err error)

Run starts the application and blocks until it stops, either due to an internal error or context cancellation. It returns any start/stop error or the first error that triggered shutdown.

func (*App) Shutdown

func (app *App) Shutdown(ctx context.Context) (err error)

Shutdown requests a graceful stop of the application and waits for it to finish or for ctx to time out. Equivalent to ShutdownCause(ctx, nil).

func (*App) ShutdownCause

func (app *App) ShutdownCause(ctx context.Context, cause error) error

ShutdownCause requests a graceful stop and records the provided cause as the terminal error returned by Err(). If a start is in progress, it first waits for it to complete. If the app is not started, it returns nil. The call blocks until the app finishes stopping or ctx is done.

func (*App) Start

func (app *App) Start(ctx context.Context) (err error)

Start builds and starts the underlying fx.App if it is not already started. If a start is already in progress, Start blocks until it completes. If the app is already started, Start is a no-op and returns nil. The method returns quickly and runs shutdown monitoring in the background; use Done() to wait for the app to stop.

func (*App) Validate

func (app *App) Validate() error

Validate checks the dependency graph by running fx.ValidateApp on the accumulated options without building or starting the app.

func (*App) WithOpts

func (app *App) WithOpts(opts ...fx.Option) *App

WithOpts appends additional fx.Options to the application before it is built. It returns the same *App to allow method chaining.

type Config

type Config struct {
	// StartTimeout is the maximum duration to wait for the application's services to start.
	StartTimeout time.Duration `envconfig:"APP_START_TIMEOUT" default:"10s"`
	// ShutdownTimeout is the maximum duration to wait for the application's services to shut down gracefully.
	ShutdownTimeout time.Duration `envconfig:"APP_SHUTDOWN_TIMEOUT" default:"10s"`
}

type RunnerFunc

type RunnerFunc func(ctx context.Context) error

func (RunnerFunc) Run

func (f RunnerFunc) Run(ctx context.Context) error

type Shutdowner

type Shutdowner interface {
	// Shutdown requests a graceful stop and waits until complete or ctx timeout.
	Shutdown(ctx context.Context) error
	// ShutdownCause requests a graceful stop and records err as the terminal cause.
	ShutdownCause(ctx context.Context, err error) error
}

Shutdowner allows any component to request a graceful application shutdown.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL