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#
- ✅ Reuse utilities - Don't duplicate code across routes
- ✅ Type-safe helpers - Use TypeScript generics for flexibility
- ✅ Standardize responses - Use
commonRespsfor consistency - ✅ Log client info - Always log IP and user agent for security
- ✅ Validate environment variables - Parse and validate on startup
- ✅ Use crypto for randomness - Don't use
Math.random()for tokens
Next Steps#
- Error Handling - Use commonResps in error handlers
- Authentication - Use isRequestFromUser for ownership checks
- Complete Request Flow - See utilities in action