Public
Like
glimpse2-runbook-view-glimpse-save-login-react
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v38View latest version
Pure utility functions with no external dependencies.
Utils are pure functions that:
- No external dependencies - Never interact with APIs, databases, or storage
- Deterministic - Same input always produces same output
- No side effects - Don't modify global state or external systems
- Reusable - Can be used across controllers and services
- Testable - Easy to unit test in isolation
Utils do NOT:
- Make external API calls (use services instead)
- Handle business workflows (controllers handle this)
- Manage application state or configuration
- Interact with databases or storage systems
- Data formatting →
utils/date.helpers.ts
- Validation logic →
utils/validation.helpers.ts
- String manipulation →
utils/string.helpers.ts
- Mathematical calculations →
utils/math.helpers.ts
- Type conversions →
utils/conversion.helpers.ts
- Notion API operations →
services/notion/page.service.ts
- Blob storage operations →
services/blob.service.ts
- Database operations →
services/database.service.ts
- Email sending →
services/email.service.ts
// ✅ Good: Pure function
export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
// ❌ Bad: Has side effects
export function logAndFormatDate(date: Date): string {
console.log('Formatting date'); // Side effect
return date.toISOString().split('T')[0];
}
// ✅ Good: No external calls
export function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// ❌ Bad: External API call
export async function validateEmailWithService(email: string): Promise<boolean> {
const response = await fetch(`/api/validate-email/${email}`);
return response.ok;
}
// ✅ Good: Always returns same output for same input
export function calculatePercentage(value: number, total: number): number {
return (value / total) * 100;
}
// ❌ Bad: Output depends on external state
export function calculatePercentageWithTimestamp(value: number, total: number): number {
const timestamp = Date.now(); // Non-deterministic
return ((value / total) * 100) + timestamp;
}
// Controllers use utils for data processing
import { validateEmail, formatDate } from '../utils/validation.helpers.ts';
import { getPageById } from '../services/notion/page.service.ts';
export async function processUserData(email: string, pageId: string) {
// 1. UTIL: Validate input data
if (!validateEmail(email)) {
return { success: false, error: "Invalid email format" };
}
// 2. SERVICE: Get external data
const pageResult = await getPageById(pageId);
// 3. UTIL: Format data for response
const formattedDate = formatDate(new Date(pageResult.data.created_time));
return {
success: true,
data: { ...pageResult.data, formattedDate }
};
}
// Services can use utils for data transformation
import { sanitizeInput } from '../utils/string.helpers.ts';
export async function createNotionPage(title: string) {
// UTIL: Clean input before external API call
const cleanTitle = sanitizeInput(title);
// External API call
const response = await notion.pages.create({
properties: { Title: { title: [{ text: { content: cleanTitle } }] } }
});
return createSuccessResponse(response);
}
export function extractTextFromNotionProperty(property: any, propertyName: string): string;
export function findPropertyByNames(properties: any, possibleNames: string[]): { name: string; property: any } | null;
export function extractAgentProperties(agentPage: any): AgentProperties;
export function generateSessionId(): string;
export function generateBlobKey(type: string, id: string): string;
export function parseBlobKey(key: string): { type: string; id: string } | null;
export function generateRandomId(): string;
export function validateEmail(email: string): boolean;
export function validateUrl(url: string): boolean;
export function validateUuid(id: string): boolean;
export function isNotEmpty(value: string): boolean;
export function validatePhoneNumber(phone: string): boolean;
export function validateEmail(email: string): boolean;
export function validateUrl(url: string): boolean;
export function validatePhoneNumber(phone: string): boolean;
export function validateUuid(id: string): boolean;
export function isNotEmpty(value: string): boolean;
export function formatDate(date: Date): string;
export function parseIsoDate(isoString: string): Date;
export function addDays(date: Date, days: number): Date;
export function isExpired(date: Date, maxAgeMinutes: number): boolean;
export function getTimestamp(): string;
export function sanitizeInput(input: string): string;
export function truncateText(text: string, maxLength: number): string;
export function slugify(text: string): string;
export function capitalizeWords(text: string): string;
export function extractDomain(email: string): string;
export function arrayToObject<T>(array: T[], keyField: string): Record<string, T>;
export function objectToArray<T>(obj: Record<string, T>): T[];
export function filterObject<T>(obj: Record<string, T>, predicate: (value: T) => boolean): Record<string, T>;
export function mapObjectValues<T, U>(obj: Record<string, T>, mapper: (value: T) => U): Record<string, U>;
export function clamp(value: number, min: number, max: number): number;
export function roundToDecimals(value: number, decimals: number): number;
export function calculatePercentage(value: number, total: number): number;
export function generateRandomId(): string;
export function isString(value: unknown): value is string;
export function isNumber(value: unknown): value is number;
export function isObject(value: unknown): value is Record<string, unknown>;
export function isArray(value: unknown): value is unknown[];
export function hasProperty<T extends Record<string, unknown>>(obj: T, key: string): key is keyof T;
Since utils are pure functions, they're easy to test:
// validation.helpers.test.ts
import { validateEmail } from './validation.helpers.ts';
describe('validateEmail', () => {
test('returns true for valid email', () => {
expect(validateEmail('user@example.com')).toBe(true);
});
test('returns false for invalid email', () => {
expect(validateEmail('invalid-email')).toBe(false);
});
});
- Each function should do one thing well
- Avoid complex multi-step operations
- Prefer composition over large functions
- Provide proper type annotations
- Use generic types where appropriate
- Export types alongside functions
- Add JSDoc comments for complex algorithms
- Explain business rules or edge cases
- Provide usage examples
- Don't import services or external libraries
- Keep functions self-contained
- Use only built-in JavaScript/TypeScript features
- Validate inputs and throw descriptive errors
- Use type guards to ensure type safety
- Handle edge cases gracefully
export function calculatePercentage(value: number, total: number): number {
if (typeof value !== 'number' || typeof total !== 'number') {
throw new Error('Both value and total must be numbers');
}
if (total === 0) {
throw new Error('Total cannot be zero');
}
return (value / total) * 100;
}