Services
Services in Wails v3 provide a powerful way to extend the functionality of your application. They allow you to create modular, reusable components that can be easily integrated into your Wails application.
Overview
Services are designed to encapsulate specific functionality and can be registered with the application at startup. They can handle various tasks such as file serving, database operations, logging, and more. Services can also interact with the application lifecycle and respond to HTTP requests.
Creating a Service
To create a service, you simply define a struct. Here’s a basic structure of a service:
type MyService struct { // Your service fields}
func NewMyService() *MyService { // Initialize and return your service}
func (s *MyService) Greet(name string) string { return fmt.Sprintf("Hello, %s!", name)}
This service has a single method, Greet
, which accepts a name and returns a
greeting.
Registering a Service
To register a service with the application, you need to provide an instance of
the service to the Services
field of the application.Options
struct.
All services need to be wrapped by an application.NewService
call.
Here’s an example:
app := application.New(application.Options{ Services: []application.Service{ application.NewService(NewMyService()), },})
This registers the NewMyService
function as a service with the application.
Services may also be registered with additional options:
app := application.New(application.Options{ Services: []application.Service{ application.NewServiceWithOptions(NewMyService(), application.ServiceOptions{ // ... }) }})
ServiceOptions has the following fields:
- Name - Specify a custom name for the Service
- Route - A route to bind the Service to the frontend (more on this below)
After the application has been created but not yet started,
you can register more services using the RegisterService
method.
This is useful when you need to feed a service some value
that is only available after the application has been created.
For example, let’s wire application’s logger into your own service:
app := application.New(application.Options{})
app.RegisterService(application.NewService(NewMyService(app.Logger)))
// ...
err := app.Run()
Services may only be registered before running the application:
RegisterService
will panic if called after the Run
method.
Optional Methods
Services can implement optional methods to hook into the application lifecycle.
ServiceName
func (s *Service) ServiceName() string
This method returns the name of the service. By default, it will the struct name of the Service but can be
overridden with the Name
field of the ServiceOptions
.
It is used for logging purposes only.
ServiceStartup
func (s *Service) ServiceStartup(ctx context.Context, options application.ServiceOptions) error
This method is called when the application is starting up. You can use it to
initialize resources, set up connections, or perform any necessary setup tasks.
The context is the application context that will be canceled upon shutdown,
and the options
parameter provides additional information about the service.
Services are initialised in the exact order of registration:
first those listed in the Services
field of the application.Options
struct,
then those added through the RegisterService
method.
ServiceShutdown
func (s *Service) ServiceShutdown() error
This method is called when the application is shutting down. Use it to clean up resources, close connections, or perform any necessary cleanup tasks.
Services are shut down in reverse registration order.
The application context will be canceled before ServiceShutdown
is called.
ServeHTTP
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request)
If your service needs to handle HTTP requests, implement this method. It allows your service to act as an HTTP handler. The route of the handler is defined in the service options:
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{ RootPath: rootPath,}), application.ServiceOptions{ Route: "/files",})
Example: File Server Service
Let’s look at a simplified version of the fileserver
service as an example:
type Service struct { config *Config fs http.Handler}
func New(config *Config) *Service { return &Service{ config: config, fs: http.FileServer(http.Dir(config.RootPath)), }}
func (s *Service) ServiceName() string { return "github.com/wailsapp/wails/v3/services/fileserver"}
func (s *Service) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { // Any initialization code here return nil}
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.fs.ServeHTTP(w, r)}
We can now use this service in our application:
app := application.New(application.Options{ Services: []application.Service{ application.NewServiceWithOptions(fileserver.New(&fileserver.Config{ RootPath: rootPath, }), application.ServiceOptions{ Route: "/files", }), },})
All requests to /files
will be handled by the fileserver
service.
Application Lifecycle and Services
- During application initialization, services are registered with the application.
- When the application starts (
app.Run()
), theServiceStartup
method of each service is called with the application context and service options. - Throughout the application’s lifetime, services can perform their specific tasks.
- If a service implements
ServeHTTP
, it can handle HTTP requests at the specified path. - When the application is shutting down, the
ServiceShutdown
method of each service is called as well as the context being cancelled.