Service Provider
Introduction
Service providers are the main way to bootstrap features in JCC Express MVC. Each provider is a class that receives the Application (the service container) and uses register() to bind services and boot() to run logic after all providers have registered—mirroring Laravel’s register / boot split.
Listing providers in bootstrap/providers.ts and implementing them under app/Providers/ keeps startup explicit and ordered.
What a provider does
A provider connects the framework to your app:
- Register container bindings (
bind,singleton,instance) so other code canresolvethem. - Boot behavior that depends on those bindings (routes are not registered here—
RouteServiceProviderloads route files duringapp.run(), but yourboot()can useGate, events, or other services already on the container).
The abstract base class lives in jcc-express-mvc (ServiceProvider). Extend it from jcc-express-mvc/Core/Provider (re-export) or the underlying lib/Providers/ServiceProvider.
The ServiceProvider base class
constructor(protected app: Application)— Stores the application instance asthis.app.abstract register(): void— You must implement this. Put container registrations here only; avoid resolving services that other providers have not registered yet.boot(): void | Promise<void>— Optional override. Runs after every provider’sregister()has completed (see Whenregisterandbootrun). Use forGate.define, subscribing to events, or resolving bindings that need the full container.listen/subscribe— Optional maps used whenbootListenersorbootSubscribersis set on the provider so theApplicationwires event listeners or subscribers during registration.
Registration order (ApplicationBuilder)
In withProviders(), the builder does not use your array alone. It builds this chain:
DatabaseServiceProvider— Always first (database-related setup).- Your providers from
bootstrap/providers.ts— IfAuthServiceProvider(framework) is in the list but not first, the builder moves it to the front of your list so auth is set up early. QueueServiceProvider— Always last in the chain.
That order matters when one provider’s register() assumes another binding already exists.
When register and boot run
During bootstrap/app build:
- For each provider class in the chain,
new Provider(app)is called, thenregister()runs immediately. - If the provider sets
bootSubscribers/bootListeners, the app may callsubscribers()/listeners()right after that provider registers.
boot() for all providers:
- When
app.boot()runs (for example fromapp.run()if the app was not yet booted), the application loopsthis.providersand callsboot()on each in registration order.
Late registration:
- If a provider is registered after the application is already
booted,register()still runs, andbootProvideris invoked immediately for that provider so it does not miss the boot phase.
Writing a provider
- Create a class under
app/Providers/extendingServiceProvider. - Implement
register()withthis.app.bind,singleton, etc. - Override
boot()when you need work after allregister()methods have finished. - Add the class to the
providersarray exported frombootstrap/providers.ts(already imported bybootstrap/app.tsviawithProviders).
Keep register() free of heavy side effects and of resolve() calls that depend on providers later in the chain unless you know the order.
Example
Framework providers you should know about
DatabaseServiceProvider— Database / ORM wiring; runs before your list.AuthServiceProvider(when included) — Intended to run early among your providers.QueueServiceProvider— Queue binding; runs after your list.RouteServiceProvider— Not in yourbootstrap/providers.tslist by default; it is registered as a singleton onApplicationandloadRoutes()is invoked fromapp.run()to loadroute/web,route/api, etc.
Your AppServiceProvider (and any custom providers) are where application-specific bindings and boot logic belong.
