Exercices â Module 03
Modules & Dependency Injection · 5 exercices pratiques
Module partagé
Créez un LoggerModule avec un LoggerService personnalisé. Exportez le service et importez le module dans deux modules différents (UsersModule et ProductsModule).
Voir la solution
@Injectable()
export class LoggerService {
private logs: string[] = [];
log(context: string, message: string) {
const entry = `[${new Date().toISOString()}] [${context}] ${message}`;
this.logs.push(entry);
console.log(entry);
}
getLogs() { return [...this.logs]; }
}
@Module({
providers: [LoggerService],
exports: [LoggerService], // Rendu disponible aux autres modules
})
export class LoggerModule {}
@Module({
imports: [LoggerModule], // AccĂšs Ă LoggerService
providers: [UsersService],
})
export class UsersModule {}
Factory provider asynchrone
Créez un provider 'EXTERNAL_API' avec useFactory asynchrone qui simule la récupération d'une configuration depuis une API externe (avec un délai de 100ms).
Voir la solution
@Module({
providers: [
{
provide: 'EXTERNAL_API',
useFactory: async (config: ConfigService) => {
// Simuler un appel API asynchrone
await new Promise(res => setTimeout(res, 100));
return {
baseUrl: config.get('API_URL', 'https://api.example.com'),
apiKey: config.get('API_KEY'),
timeout: 5000,
};
},
inject: [ConfigService],
},
],
exports: ['EXTERNAL_API'],
})
export class ApiModule {}
// Injection dans un service
@Injectable()
export class ApiService {
constructor(
@Inject('EXTERNAL_API')
private readonly apiConfig: { baseUrl: string; apiKey: string; timeout: number },
) {}
}
Module dynamique avec options
CrĂ©ez un ThrottlerModule dynamique avec register(options) permettant de configurer la limite de requĂȘtes par IP : nombre max de requĂȘtes et fenĂȘtre temporelle.
Voir la solution
interface ThrottlerOptions {
limit: number;
windowMs: number;
}
@Module({})
export class ThrottlerModule {
static register(options: ThrottlerOptions): DynamicModule {
return {
module: ThrottlerModule,
providers: [
{ provide: 'THROTTLER_OPTIONS', useValue: options },
ThrottlerService,
],
exports: [ThrottlerService],
};
}
}
// app.module.ts
@Module({
imports: [
ThrottlerModule.register({ limit: 100, windowMs: 60 * 1000 }), // 100 req/min
],
})
export class AppModule {}
Module global @Global()
Créez un CoreModule global contenant un AppConfigService et un DateService. Vérifiez que ces services sont disponibles dans UsersService sans import explicite de CoreModule.
Voir la solution
@Injectable()
export class DateService {
now(): Date { return new Date(); }
format(date: Date): string { return date.toISOString().split('T')[0]; }
addDays(date: Date, days: number): Date {
const d = new Date(date);
d.setDate(d.getDate() + days);
return d;
}
}
@Global()
@Module({
providers: [AppConfigService, DateService],
exports: [AppConfigService, DateService],
})
export class CoreModule {}
// AppModule importe CoreModule une seule fois
@Module({ imports: [CoreModule, UsersModule] })
export class AppModule {}
// UsersService peut injecter DateService directement (pas besoin d'importer CoreModule)
@Injectable()
export class UsersService {
constructor(private readonly dateService: DateService) {}
}
Résoudre une dépendance circulaire
UsersService dépend de AuthService (pour vérifier les tokens) et AuthService dépend de UsersService (pour trouver les utilisateurs). Résolvez cette dépendance circulaire avec forwardRef().
Voir la solution
// users.service.ts
@Injectable()
export class UsersService {
constructor(
@Inject(forwardRef(() => AuthService))
private readonly authService: AuthService,
@InjectRepository(User) private userRepo: Repository<User>,
) {}
async findByValidToken(token: string): Promise<User | null> {
const userId = await this.authService.validateToken(token);
if (!userId) return null;
return this.findOne(userId);
}
}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
@Inject(forwardRef(() => UsersService))
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
) {}
async validateUser(email: string, password: string) {
const user = await this.usersService.findByEmail(email);
// ...
}
}
// Modules
@Module({
imports: [forwardRef(() => AuthModule)],
exports: [UsersService],
})
export class UsersModule {}
@Module({
imports: [forwardRef(() => UsersModule)],
exports: [AuthService],
})
export class AuthModule {}