Skip to content

Service Providers

Service providers are where your application is assembled. Every binding, every route file and every piece of startup wiring is registered by one.

Creating a Provider

class AppServiceProvider extends ServiceProvider {
AppServiceProvider(super.app);
@override
void register() {
this.app.singleton<PostRepository>((c) => PostRepository(c.make<Database>()));
}
@override
Future<void> boot() async {
await this.app.make<Database>().connect();
}
@override
Future<void> shutdown() => this.app.make<Database>().close();
}

Generate one with:

Terminal window
maat make:provider BillingServiceProvider

register and boot

register binds things. boot uses them.

Every provider’s register runs before any provider’s boot, which is what makes order-independence possible: by the time anything boots, every binding in the application exists. Inside register, only bind — never resolve a service another provider might not have registered yet.

boot may be async, and each is awaited in turn, so a provider that needs a connection open before the first request can open it there.

shutdown may also be async. It runs in reverse provider order after HTTP stops accepting requests, which is where a provider closes pools, sockets, and other owned resources.

Registering Providers

Providers are listed in bootstrap/app.dart:

.withProviders([
AppServiceProvider.new,
(app) => RouteServiceProvider(app, api: apiRoutes, web: webRoutes),
])

Each entry is a function from Application to a provider. AppServiceProvider.new is the constructor tear-off, which suffices when the provider takes only the application; use a closure when it needs more.

They run in the order listed.

The Route Service Provider

The skeleton’s RouteServiceProvider loads your route files in boot, after every binding exists:

class RouteServiceProvider extends ServiceProvider {
RouteServiceProvider(super.app, {required this.api, required this.web});
final void Function() api;
final void Function() web;
@override
void boot() {
Route.prefix('/api').group(api);
Route.group(web);
}
}

Routes must be declared after the application is built. The Route facade resolves the router from the current application, so calling it before create() throws a StateError.

The this.app Trap

Inside a provider, always write this.app:

this.app.singleton<PostRepository>((c) => PostRepository()); // correct
app.singleton<PostRepository>((c) => PostRepository()); // does not compile

A bare app binds to the global app<T>() helper function, not to the app field inherited from ServiceProvider — Dart resolves an unqualified name through the enclosing library scope before it consults inherited members. The error you get is The method 'singleton' isn't defined for the type 'Function', which does not point at the real cause.

Laravel has the same shape for a different reason: $this->app->singleton(...).