This tutorial walks you through building a working application with @alt-javascript. By the end, you'll have a service with dependency injection, configuration, logging, and lifecycle hooks.
- Node.js 18 or later
- npm
mkdir my-app && cd my-app
npm init -y
npm install @alt-javascript/boot @alt-javascript/cdi @alt-javascript/config @alt-javascript/loggerAdd "type": "module" to your package.json (the framework uses ES modules).
Create services.js:
export class GreetingRepository {
constructor() {
this.greetings = ['Hello', 'Hi', 'Hey'];
}
getRandom() {
return this.greetings[Math.floor(Math.random() * this.greetings.length)];
}
}
export class GreetingService {
constructor() {
this.greetingRepository = null; // will be autowired
}
greet(name) {
const greeting = this.greetingRepository.getRandom();
return `${greeting}, ${name}!`;
}
}The key pattern: GreetingService declares a greetingRepository property initialised to null. The IoC container matches this property name to the registered GreetingRepository component and injects the singleton instance.
Create config/default.json:
{
"logging": {
"level": {
"ROOT": "info"
}
},
"app": {
"name": "My First App"
}
}Create app.js:
import { Boot } from '@alt-javascript/boot';
import { ApplicationContext, Context, Singleton } from '@alt-javascript/cdi';
import { EphemeralConfig } from '@alt-javascript/config';
import { GreetingRepository, GreetingService } from './services.js';
const config = new EphemeralConfig({
logging: { level: { ROOT: 'info' } },
app: { name: 'My First App' },
});
Boot.boot({ config });
const context = new Context([
new Singleton(GreetingRepository),
new Singleton(GreetingService),
]);
const appCtx = new ApplicationContext({ contexts: [context], config });
await appCtx.start();
const service = appCtx.get('greetingService');
console.log(service.greet('World'));Run it:
node app.js
# Output: Hello, World! (or Hi, World! or Hey, World!)Components can implement init() for startup logic and destroy() for cleanup:
export class GreetingService {
constructor() {
this.greetingRepository = null;
this.callCount = 0;
}
init() {
console.log('GreetingService initialized');
}
greet(name) {
this.callCount++;
const greeting = this.greetingRepository.getRandom();
return `${greeting}, ${name}!`;
}
destroy() {
console.log(`GreetingService shutting down. Total greetings: ${this.callCount}`);
}
}The container calls init() during the prepare phase and registers destroy() for process shutdown.
For dependencies that must be available at construction time:
export class NotificationService {
constructor(greetingService) {
this.greetingService = greetingService;
}
notify(name) {
return `[NOTIFICATION] ${this.greetingService.greet(name)}`;
}
}Register with constructorArgs:
const context = new Context([
new Singleton(GreetingRepository),
new Singleton(GreetingService),
{ Reference: NotificationService, name: 'notificationService', constructorArgs: ['greetingService'] },
]);- Dependency Injection — scopes, explicit wiring, profiles
- Configuration — property sources, profiles, environment variables
- Lifecycle & Events — events, BeanPostProcessor, lifecycle interfaces
- Advanced Features — AOP, auto-discovery, conditional beans