Service structs

Encore lets you define a type, called a service struct, to represent your running service. This lets you define an initialization function (similar to the main function in regular Go programs).

You can also define API endpoints as methods on the service struct type, enabling you to use dependency injection for testing purposes.

It works by defining a struct type of your choice (typically called Service) and declaring it with //encore:service. Then, you can define a special function named initService (or initWhatever if you named the type Whatever) that gets called by Encore to initialize your service when it starts up.

It looks like this:

//encore:service type Service struct { // Add your dependencies here } func initService() (*Service, error) { // Write your service initialization code here. } //encore:api public func (s *Service) MyAPI(ctx context.Context) error { // ... }
Related example
Event-driven example application using service structs.
$ encore app create --example=uptime

Calling APIs defined on service structs

When using a service struct like above, Encore will create a file named encore.gen.go in your service directory. This file contains package-level functions for the APIs defined as methods on the service struct. In the example above, you would see:

// Code generated by encore. DO NOT EDIT. package email import "context" // These functions are automatically generated and maintained by Encore // to simplify calling them from other services, as they were implemented as methods. // They are automatically updated by Encore whenever your API endpoints change. func Send(ctx context.Context, p *SendParams) error { // The implementation is elided here, and generated at compile-time by Encore. return nil }

These functions are generated in order to allow other services to keep calling your APIs as package-level functions, in the same way as before: email.Send(...). This means other services do not need to care about whether you're using Dependency Injection internally. You must always use these generated package-level functions for making API calls.

Please note

Encore will automatically generate these files and keep them up to date whenever your code changes. There is no need to manually invoke anything to regenerate this code.

Encore adds all encore.gen.go files to your .gitignore since you typically don't want to commit them to your repository; doing so ends up creating a lot of unnecessary merge conflicts.

However, in some cases when running third-party linters in a CI/CD environment it can be helpful to generate these wrappers to make the linter happy. You can do that by invoking encore gen wrappers.

Graceful Shutdown

When defining a service struct, Encore supports notifying your service when it's time to gracefully shut down. This works by having your service struct implement the method func (s *Service) Shutdown(force context.Context).

If that method exists, Encore will call it when it's time to begin gracefully shutting down. Initially the shutdown is in "graceful mode", which means that you have a few seconds to complete ongoing work.

The provided force context is canceled when the graceful shutdown window is over, and it's time to forcefully shut down. How much time you have from when Shutdown is called to when forceful shutdown begins depends on the cloud provider and the underlying infrastructure. Typically it's in the range 5-30 seconds.

Please note

Encore automatically handles graceful shutdown of all Encore-managed functionality, such as HTTP servers, database connection pools, Pub/Sub message receivers, distributed tracing recorders, and so on.

The graceful shutdown functionality is provided if you have additional, non-Encore-related resources that need graceful shutdown.

Note that graceful shutdown in Encore is cooperative: Encore will wait indefinitely for your Shutdown method to return. If your Shutdown method does not return promptly after the force context is closed, the underlying infrastructure at your cloud provider will typically force-kill your service, which can lead to lingering connections and other such issues.

In summary, when your Shutdown(force context.Context) function is called:

  • Immediately begin gracefully shutting down
  • When the force context is canceled, you should forcefully shut down the resources that haven't yet completed their shutdown
  • Wait until the shutdown is complete before returning from the Shutdown function