Scripts helpers
This directory contains reusable utility functions and helpers for the application. All helpers are designed to be type-safe and well-tested.
Centralised Imports
All helpers are available through a central index file, allowing for clean imports:
// Import multiple helpers from the index
import { devLog, safeString, formatDate } from '#src/scripts/helpers/index.js';
// Instead of importing from individual files
import { devLog } from '#src/scripts/helpers/devLogger.js';
import { safeString } from '#src/scripts/helpers/dataTransformers.js';
import { formatDate } from '#src/scripts/helpers/dateFormatter.js';
Available Helpers
๐ Development Logging (devLogger.ts)
Environment-aware logging utilities that only output to console in development mode, keeping production logs clean.
Functions
devLog(message: string)- Development-only console.logdevWarn(message: string)- Development-only console.warndevError(message: string)- Development-only console.errordevDebug(message: string)- Development-only console.debugisDevelopment(): boolean- Check if running in development mode
Usage
import { devLog, devWarn, devError, isDevelopment } from '#src/scripts/helpers/index.js';
// These will only appear in development mode
devLog('Initializing component...');
devWarn('Deprecated feature used');
devError('Failed to load data');
// Conditional logic based on environment
if (isDevelopment()) {
// Development-only code
}
Environment Detection
Logs appear when:
NODE_ENV === 'development'NODE_ENVis undefined (default development behaviour)
Logs are suppressed when:
NODE_ENV === 'production'NODE_ENV === 'test'
๐ง Data Transformers (dataTransformers.ts)
Type-safe utilities for transforming and validating data from JSON fixtures and API responses.
Functions
safeString(value: unknown): string- Safely convert unknown value to stringsafeOptionalString(value: unknown): string | undefined- Safely convert to optional stringisRecord(value: unknown): value is Record<string, unknown>- Type guard for object recordssafeStringFromRecord(obj: unknown, key: string): string | null- Safely extract string from recordhasProperty(obj: unknown, key: string): obj is Record<string, unknown>- Check if object has property
Usage
import {
safeString,
safeOptionalString,
isRecord,
safeStringFromRecord,
hasProperty
} from '#src/scripts/helpers/index.js';
// Transform API data safely
function transformCaseData(rawData: unknown) {
if (!isRecord(rawData)) {
throw new Error('Invalid data format');
}
return {
name: safeString(rawData.name),
description: safeOptionalString(rawData.description),
dateOfBirth: safeString(rawData.dateOfBirth)
};
}
Type Safety
All functions handle null, undefined, and unexpected types gracefully, returning sensible defaults or typed guards.
๐
Date Formatter (dateFormatter.ts)
Consistent date formatting utilities for UI components and tables.
Functions
formatDate(dateString: string): string- Format ISO date string to “DD MMM YYYY” format
Usage
import { formatDate } from '#src/scripts/helpers/index.js';
// Format dates for display
const displayDate = formatDate('2023-01-06T00:00:00.000Z');
// Returns: "06 Jan 2023"
// Use in Nunjucks templates via filter
// {{ dateReceived | formatDate }}
Format
- Input: ISO date string (
"2023-01-06T00:00:00.000Z") - Output: Human-readable format (
"06 Jan 2023") - Locale: British English (
en-GB) - Fallback: Returns original string if parsing fails
โ ๏ธ Error Handler (errorHandler.ts)
Comprehensive error handling utilities for API requests and network operations, providing user-friendly error messages and structured logging.
Functions
extractErrorMessage(error: unknown): string- Extract user-friendly error message from various error typescreateProcessedError(error: unknown, context: string): Error- Create processed error with user-friendly message for global handlerextractAndLogError(error: unknown, context: string): string- Extract error message and log it with context (for API services)isHttpError(error: unknown, status: number): boolean- Check if error is a specific HTTP status codeisAuthError(error: unknown): boolean- Check if error is authentication failure (401)isForbiddenError(error: unknown): boolean- Check if error is forbidden access (403)isNotFoundError(error: unknown): boolean- Check if error is not found (404)isServerError(error: unknown): boolean- Check if error is server error (5xx)
Usage
import {
extractErrorMessage,
createProcessedError,
extractAndLogError,
isAuthError,
isServerError
} from '#src/scripts/helpers/index.js';
// For route handlers - create processed errors for global handler
try {
await loadUserData();
} catch (error) {
const processedError = createProcessedError(error, 'loading user data');
next(processedError); // Pass to global error handler
}
// For API services - extract and log errors for service responses
try {
const response = await externalApiCall();
return { data: response, status: 'success' };
} catch (error) {
const errorMessage = extractAndLogError(error, 'External API error');
return { data: null, status: 'error', message: errorMessage };
}
// Basic error message extraction
try {
await apiCall();
} catch (error) {
const userMessage = extractErrorMessage(error);
// Returns: "Authentication failed. Please log in again." for 401 errors
// Handle specific error types
if (isAuthError(error)) {
// Redirect to login
} else if (isServerError(error)) {
// Show retry option
}
}
Error Types
HTTP Status Codes:
400โ “Invalid request. Please check your input and try again.”401โ “Authentication failed. Please log in again.”403โ “You do not have permission to access this resource.”404โ “The requested information could not be found.”500โ “Internal server error. Please try again later.”502/503โ “Service temporarily unavailable. Please try again later.”
Network Errors:
ECONNREFUSEDโ “Unable to connect to the service. Please try again later.”ETIMEDOUTโ “Request timed out. Please try again.”ENOTFOUNDโ “Service not found. Please check your connection and try again.”
Features:
- Extracts custom error messages from API responses
- Provides fallback messages for unknown errors
- Includes structured logging for debugging
- Type-safe error detection with proper type guards
๐ง Error Handling Function Selection Guide
Choose the appropriate error handling function based on your use case:
Use extractAndLogError()
- When: In service layer methods that make API calls
- Purpose: Process errors and return detailed information for further handling
- Example Use Cases:
apiService.getCases()- needs to process and return error detailsauthService.validateToken()- requires structured error information- Any service method that needs to handle errors and pass details to caller
Use createProcessedError()
- When: In route handlers or middleware that need to return errors to client
- Purpose: Create user-friendly error objects ready for HTTP responses
- Example Use Cases:
- Express route handlers returning errors to frontend
- Middleware that processes errors for API responses
- Any endpoint that needs to send formatted errors to client
Use extractErrorMessage()
- When: You only need the error message string without additional processing
- Purpose: Extract clean, user-friendly error messages from various error types
- Example Use Cases:
- Simple error logging without structured information
- Template rendering where only message text is needed
- Basic error display in UI components
๐ Error Handling Pattern Examples
// Service Layer Pattern
export async function getCases(params: CaseApiParams) {
try {
const response = await axiosWrapper.get<ApiResponse<CaseData>>(endpoint, { params });
return response.data;
} catch (error) {
const { message, statusCode } = extractAndLogError(error, 'getCases');
throw new Error(message); // Re-throw with processed message
}
}
// Route Handler Pattern
router.get('/cases', async (req, res, next) => {
try {
const cases = await apiService.getCases(req.query);
res.json(cases);
} catch (error) {
const processedError = createProcessedError(error, 'GET /cases');
next(processedError); // Pass to error middleware
}
});
// Simple Message Extraction
function displayError(error: unknown) {
const message = extractErrorMessage(error);
console.log(`Error: ${message}`);
}
Implementation Guidelines
๐ฏ Best Practices
Use the centralised index import
// โ
Correct - Import from index
import { devLog, safeString, formatDate } from '#src/scripts/helpers/index.js';
// โ Avoid individual file imports
import { devLog } from '#src/scripts/helpers/devLogger.js';
import { safeString } from '#src/scripts/helpers/dataTransformers.js';
Use TypeScript path aliases
// โ
Use alias
import { safeString } from '#src/scripts/helpers/index.js';
// โ Avoid relative paths
import { safeString } from '../../helpers/dataTransformers.js';
Prefer specific imports
// โ
Import only what you need
import { devLog, devError } from '#src/scripts/helpers/index.js';
// โ Avoid namespace imports
import * as helpers from '#src/scripts/helpers/index.js';
๐ Usage Patterns
Replacing Console Calls
When refactoring existing code:
// Before
console.log('Debug message');
console.warn('Warning message');
console.error('Error message');
// After
import { devLog, devWarn, devError } from '#src/scripts/helpers/index.js';
devLog('Debug message');
devWarn('Warning message');
devError('Error message');
Data Validation
When working with API responses or JSON data:
import { isRecord, safeString } from '#src/scripts/helpers/index.js';
function processApiResponse(data: unknown) {
if (!isRecord(data)) {
throw new Error('Invalid response format');
}
return {
id: safeString(data.id),
name: safeString(data.name),
// ... other fields
};
}
Error Handling
When making API calls or handling errors:
import { extractErrorMessage, isAuthError, isServerError } from '#src/scripts/helpers/index.js';
async function fetchUserData() {
try {
const response = await apiCall();
return response;
} catch (error) {
const userMessage = extractErrorMessage(error);
// Handle specific error types
if (isAuthError(error)) {
// Redirect to login page
window.location.href = '/login';
} else if (isServerError(error)) {
// Show retry option
showRetryDialog(userMessage);
} else {
// Show general error message
showErrorMessage(userMessage);
}
throw error; // Re-throw for caller to handle
}
}
๐งช Testing
All helpers should be thoroughly tested:
- Unit tests in
tests/unit/src/scripts/helpers/ - Test files follow pattern
{helperName}.spec.ts - Use Chai assertions and Sinon for mocking
- Include edge cases and error conditions
๐ File Organization
src/scripts/helpers/
โโโ index.ts # Central export file
โโโ dataTransformers.ts # Data validation and transformation
โโโ dateFormatter.ts # Date formatting utilities
โโโ devLogger.ts # Development logging
โโโ errorHandler.ts # Error handling and user-friendly messages
โโโ [future-helper].ts # Additional helpers as needed
๐ Adding New Helpers
When creating new helpers:
- Create the helper file with clear JSDoc comments
- Add comprehensive unit tests in the test directory
- Update this README with documentation
- Follow existing patterns for imports and exports
- Consider environment awareness (development vs production)
Template for New Helper
/**
* [Helper Name]
*
* Brief description of what this helper does.
*/
/**
* Function description
* @param {Type} param Parameter description
* @returns {ReturnType} Return value description
*/
export function helperFunction(param: Type): ReturnType {
// Implementation
}
Quick Reference
| Helper | Purpose | Key Functions |
|---|---|---|
devLogger |
Development logging |
devLog, devWarn, devError
|
dataTransformers |
Data validation/transformation |
safeString, isRecord, safeStringFromRecord
|
dateFormatter |
Date formatting | formatDate |
errorHandler |
Error handling/user-friendly messages |
extractErrorMessage, createProcessedError, extractAndLogError, isAuthError, isServerError
|
Import any helper with:
import { functionName } from '#src/scripts/helpers/index.js';