Skip to content

Helper Functions & Utilities#

Common utility functions for Express applications.

Get Client IP#

Handles proxy headers to get the real client IP:

import type { Request } from "express";

export function getClientIp(req: Request): string {
  // Check X-Forwarded-For header (from proxy/load balancer)
  if (req.headers["x-forwarded-for"]) {
    return (req.headers["x-forwarded-for"] as string).split(",")[0]!.trim();
  }

  // Check X-Real-IP header
  if (req.headers["x-real-ip"]) {
    return req.headers["x-real-ip"] as string;
  }

  // Fallback to direct connection IP
  return req.ip!;
}

// Usage
logger.info("Request received", {
  ip: getClientIp(req),
  userAgent: req.get("User-Agent"),
});

Create Slug#

Convert titles to URL-friendly slugs:

export function createSlug(title: string): string {
  return title
    .toLowerCase()              // Convert to lowercase
    .trim()                     // Remove leading/trailing spaces
    .replace(/[^\w\s-]/g, "")   // Remove non-word characters
    .replace(/\s+/g, "-")       // Replace spaces with hyphens
    .replace(/-+/g, "-");       // Collapse multiple hyphens
}

// Usage
createSlug("Hello World! 123")  // "hello-world-123"
createSlug("  Test   Title  ")  // "test-title"

Random String#

Generate secure random strings:

import * as crypto from "node:crypto";

export function randString(length: number = 20): string {
  return crypto.randomBytes(length).toString("hex");
}

// Usage
const token = randString();     // 40 char hex string
const shortId = randString(8);  // 16 char hex string

Filter Undefined Values#

Remove undefined values from objects:

export function filterUndefined<T>(obj: T): {
  [K in keyof T]: Exclude<T[K], undefined>;
} {
  const result = {} as any;
  for (const key in obj) {
    if (obj[key] !== undefined) {
      result[key] = obj[key];
    }
  }
  return result;
}

// Usage
const data = { name: "Alice", age: undefined, email: "test@example.com" };
const filtered = filterUndefined(data);
// { name: "Alice", email: "test@example.com" }

Request Ownership Check#

Check if request is from resource owner:

export function isRequestFromUser(req: Request, userId: string): boolean {
  return req.authSession!.user.id === userId;
}

// Usage in route
router.put("/posts/:id", requireAuth(), async (req, res) => {
  const post = await getPostById(req.params.id);

  if (!isRequestFromUser(req, post.authorId)) {
    return res.status(403).json({ error: "Not authorized" });
  }

  // Update post
});

Common API Responses#

Standardized error responses:

export const commonResps = {
  internalError: {
    error: "Failed to complete request due to internal error",
    code: "internal_server_error",
  },
  unauthorized: {
    error: "Authentication required",
    code: "unauthorized",
  },
  forbidden: {
    error: "Insufficient permissions",
    code: "forbidden",
  },
  notFound: {
    error: "Resource not found",
    code: "not_found",
  },
  conflict: {
    error: "Resource already exists",
    code: "resource_conflict",
  },
  badRequest: {
    error: "Invalid request data",
    code: "bad_request",
  },
};

// Usage
import { StatusCodes } from "http-status-codes";

res.status(StatusCodes.UNAUTHORIZED).json(commonResps.unauthorized);
res.status(StatusCodes.NOT_FOUND).json(commonResps.notFound);

TypeScript Patterns#

Type Assertion#

Tell TypeScript a value is guaranteed to be a certain type:

const value = something as string;  // Guaranteed to be string
const element = document.getElementById("id") as HTMLInputElement;

Mapping Object Keys with Types#

// Remove undefined from all properties
type RequiredProps<T> = {
  [K in keyof T]: Exclude<T[K], undefined>;
};

// Example
type User = {
  name: string;
  age?: number;
  email?: string;
};

type RequiredUser = RequiredProps<User>;
// { name: string; age: number; email: string }

Explanation: - keyof T → Get all object keys ("name" | "age" | "email") - [K in keyof T] → Loop through each key - Exclude<T[K], undefined> → Remove undefined from type

Environment Variable Parsing#

Parse Comma-Separated List#

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",").filter(
  (url) => url.trim() !== ""
) || [];

// ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
// → ["http://localhost:3000", "http://localhost:5173"]

Parse Boolean#

const isProduction = process.env.NODE_ENV === "production";
const devMode = process.env.NODE_ENV !== "production";

Parse Number with Default#

const port = Number(process.env.PORT) || 3000;
const timeout = parseInt(process.env.TIMEOUT || "5000", 10);

Complete Utils File#

// utils/utils.ts
import type { Request } from "express";
import * as crypto from "node:crypto";

// IP Address
export function getClientIp(req: Request): string {
  if (req.headers["x-forwarded-for"]) {
    return (req.headers["x-forwarded-for"] as string).split(",")[0]!.trim();
  }
  if (req.headers["x-real-ip"]) {
    return req.headers["x-real-ip"] as string;
  }
  return req.ip!;
}

// Slug Generation
export function createSlug(title: string): string {
  return title
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, "")
    .replace(/\s+/g, "-")
    .replace(/-+/g, "-");
}

// Random String
export function randString(length: number = 20): string {
  return crypto.randomBytes(length).toString("hex");
}

// Filter Undefined
export function filterUndefined<T>(obj: T): {
  [K in keyof T]: Exclude<T[K], undefined>;
} {
  const result = {} as any;
  for (const key in obj) {
    if (obj[key] !== undefined) {
      result[key] = obj[key];
    }
  }
  return result;
}

// Request Checks
export function isRequestFromUser(req: Request, userId: string): boolean {
  return req.authSession!.user.id === userId;
}

// Common Responses
export const commonResps = {
  internalError: {
    error: "Failed to complete request due to internal error",
    code: "internal_server_error",
  },
  unauthorized: {
    error: "Authentication required",
    code: "unauthorized",
  },
  forbidden: {
    error: "Insufficient permissions",
    code: "forbidden",
  },
  notFound: {
    error: "Resource not found",
    code: "not_found",
  },
  conflict: {
    error: "Resource already exists",
    code: "resource_conflict",
  },
};

Best Practices#

  1. Reuse utilities - Don't duplicate code across routes
  2. Type-safe helpers - Use TypeScript generics for flexibility
  3. Standardize responses - Use commonResps for consistency
  4. Log client info - Always log IP and user agent for security
  5. Validate environment variables - Parse and validate on startup
  6. Use crypto for randomness - Don't use Math.random() for tokens

Next Steps#