CORE CONCEPTS

Providers & Tokens

Rasengan Server resolves constructor dependencies automatically, by matching parameter names against registered provider class names (no decorators, no manual token wiring for the common case).

Constructor injection
export class UserController extends Controller { constructor(private userService: UserService) { super(); } // ... }
Registering the provider
export default defineModule({ controllers: [UserController], providers: [UserService], });

The container inspects UserController's constructor source, sees a parameter named userService, and resolves it against a registered class named UserService (case-insensitive).

Provider Shapes

A provider is either a plain class, or a ProviderDefinition object for more control:

ProviderDefinition
interface ProviderDefinition { provide: any; // injection token: a class or a string useClass?: new (...args: any[]) => any; // class to instantiate (defaults to `provide`) useValue?: any; // static value, skips instantiation entirely deps?: any[]; // explicit constructor dependency tokens }
Value provider: a string token
export default defineModule({ providers: [ { provide: 'CONFIG', useValue: { apiUrl: 'https://api.example.com' } }, ], });
Class alias
export default defineModule({ providers: [{ provide: Logger, useClass: FileLogger }], });
Explicit deps (disambiguates when name-matching is ambiguous)
export default defineModule({ providers: [{ provide: EmailService, deps: ['CONFIG', SmtpClient] }], });

Resolving a String Token

Injecting a string-token provider
export class BillingService { constructor(private config: { apiUrl: string }) {} // param name must match the token, case-insensitively }

For string tokens, the constructor parameter name is matched against the token string itself (not a class name). constructor(config) resolves "CONFIG" (case-insensitive).

Singletons by Default

Every provider is a singleton, one instance per token, shared by every module that can see it. There's no scope-per-request or transient lifetime; if you need per-request state, put it on ctx.state instead (see Futon's Context).

Auto-registration

Resolving a class that was never explicitly listed in any providers array still works. The container auto-registers it as a private provider owned by whichever module triggered the resolution, then caches it as a singleton. This is how controllers themselves become resolvable without appearing in providers. Prefer explicit registration for anything you intend to export or inject elsewhere. Auto-registered providers are always private to the resolving module.

Errors You'll Actually See

[rasengan-server] Cannot resolve dependency "userService" from module "UserModule". Make sure the provider is registered in your module with a matching class name. Providers visible here: UserController
[rasengan-server] "config" is ambiguous in module "UserModule", visible from multiple modules (module "AuthModule", module "BillingModule"). Disambiguate with an explicit `deps: [...]` entry naming the exact class.

The first happens when nothing matching is registered or visible; the second when two different visible providers share a name, resolved by adding an explicit deps entry naming the exact one you want.

Controllers & Routing
Module Scoping