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:
maat make:provider BillingServiceProviderregister 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()); // correctapp.singleton<PostRepository>((c) => PostRepository()); // does not compileA 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(...).