Synthetics Coding Standards
This guide covers coding standards specific to the Statux Synthetics application.
Service Patterns
Check Validation
Always validate check targets based on type:
// TCP checks require host:port format
if (dto.checkType === CheckType.TCP) {
if (!dto.target.includes(':')) {
throw new BadRequestException('TCP target must be in host:port format');
}
}
// HTTP/HTTPS checks require valid URL
try {
new URL(dto.target);
} catch {
throw new BadRequestException('Invalid URL format');
}
State Updates
Use threshold-based state transitions:
// Check state updates
if (status === CheckStatus.UP) {
check.consecutiveSuccesses++;
check.consecutiveFailures = 0;
if (previousStatus !== CheckStatus.UP &&
check.consecutiveSuccesses >= check.recoveryThreshold) {
check.currentStatus = CheckStatus.UP;
check.lastStatusChangeAt = new Date();
// Fire webhook
}
}
Relay Token Handling
- Never log raw tokens - only log the prefix
- Store tokens as SHA-256 hashes
- Return raw token only once on creation
const rawToken = `stx_relay_${randomBytes(32).toString('hex')}`;
const tokenHash = createHash('sha256').update(rawToken).digest('hex');
const tokenPrefix = rawToken.substring(0, 12);
// Log safely
this.logger.log(`Relay created with token prefix: ${tokenPrefix}`);
Controller Patterns
Guard Usage
All authenticated endpoints should use:
@UseGuards(JwtAuthGuard, ProjectAccessGuard)
@Controller('projects/:projectId/checks')
export class ChecksController {
// ...
}
Relay endpoints use RelayAuthGuard:
@UseGuards(RelayAuthGuard)
@Controller('relay')
export class RelayController {
// ...
}
Parameter Extraction
Use decorators consistently:
@Get(':id')
findOne(
@CurrentProject() project: Project,
@Param('id') id: string,
): Promise<Check> {
return this.checksService.findOne(project.id, id);
}
DTO Patterns
Check DTOs
export class CreateCheckDto {
@IsString()
@MinLength(1)
@MaxLength(255)
name: string;
@IsEnum(CheckType)
checkType: CheckType;
@IsString()
target: string;
@IsEnum(CheckInterval)
intervalSeconds: number;
@IsOptional()
@IsUUID()
relayId?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
regions?: string[];
}
Result Submission DTOs
export class SubmitResultDto {
@IsUUID()
checkId: string;
@IsEnum(CheckStatus)
status: CheckStatus;
@IsInt()
@Min(0)
responseTimeMs: number;
@IsOptional()
@IsInt()
statusCode?: number;
@IsOptional()
@IsString()
errorMessage?: string;
@IsDateString()
executedAt: string;
}
Testing Standards
Mock Setup
const mockCheck: Partial<Check> = {
id: 'check-1',
projectId: 'proj-1',
name: 'API Health Check',
checkType: CheckType.HTTPS,
target: 'https://api.example.com/health',
currentStatus: CheckStatus.UP,
isEnabled: true,
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ChecksService,
{
provide: getRepositoryToken(Check),
useValue: {
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
// ...
},
},
],
}).compile();
});
Test Categories
- CRUD operations: Create, read, update, delete
- Validation: Target format, required fields
- State transitions: Up/down/degraded logic
- Edge cases: Relay assignment, region defaults
Error Handling
Use standard NestJS exceptions:
throw new NotFoundException('Check not found');
throw new BadRequestException('Invalid target format');
throw new ForbiddenException('Check belongs to different relay');
Logging
Use the Logger service consistently:
private readonly logger = new Logger(ChecksService.name);
// Log significant operations
this.logger.log(`Check created: ${check.id} (${check.name})`);
this.logger.warn(`Relay ${relay.id} disconnected`);
this.logger.debug(`Processing ${results.length} results`);