- Introduced backend patterns skill with guidelines on API design, database optimization, and server-side best practices. - Added coding standards skill outlining universal coding principles for TypeScript, NestJS, and Node.js development. - Implemented continuous learning skill to automatically extract reusable patterns from Cursor sessions. - Created NestJS best practices skill detailing architecture patterns, dependency injection, error handling, and security measures. - Included various rules and templates for NestJS best practices to ensure production-ready applications.
2.3 KiB
2.3 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Prefer Constructor Injection | CRITICAL | Required for proper DI and testing | dependency-injection, constructor, testing |
Prefer Constructor Injection
Always use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support.
Incorrect (property injection with hidden dependencies):
// Property injection - avoid unless necessary
@Injectable()
export class UsersService {
@Inject()
private userRepo: UserRepository; // Hidden dependency
@Inject('CONFIG')
private config: ConfigType; // Also hidden
async findAll() {
return this.userRepo.find();
}
}
// Problems:
// 1. Dependencies not visible in constructor
// 2. Service can be instantiated without dependencies in tests
// 3. TypeScript can't enforce dependency types at instantiation
Correct (constructor injection with explicit dependencies):
// Constructor injection - explicit and testable
@Injectable()
export class UsersService {
constructor(
private readonly userRepo: UserRepository,
@Inject('CONFIG') private readonly config: ConfigType,
) {}
async findAll(): Promise<User[]> {
return this.userRepo.find();
}
}
// Testing is straightforward
describe('UsersService', () => {
let service: UsersService;
let mockRepo: jest.Mocked<UserRepository>;
beforeEach(() => {
mockRepo = {
find: jest.fn(),
save: jest.fn(),
} as any;
service = new UsersService(mockRepo, { dbUrl: 'test' });
});
it('should find all users', async () => {
mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]);
const result = await service.findAll();
expect(result).toHaveLength(1);
});
});
// Only use property injection for optional dependencies
@Injectable()
export class LoggingService {
@Optional()
@Inject('ANALYTICS')
private analytics?: AnalyticsService;
log(message: string) {
console.log(message);
this.analytics?.track('log', message); // Optional enhancement
}
}
Reference: NestJS Providers