Part 4: Functional Error Handling - Railway-Oriented Programming

Introduction

My first API service used try/catch everywhere. Nested error handling made code unreadable. Exceptions flew across module boundaries. Some errors got caught twice, others not at all. I couldn't tell which functions might throw just by looking at their signatures.

I refactored to functional error handling using Either and Option types. Errors became explicit in type signatures. Functions composed cleanly through success and failure paths. Error handling became predictable, composable, and type-safe. That's when I understood Railway-oriented programming.

This article teaches you to handle errors functionally using Either/Result types, Option/Maybe patterns, Railway-oriented programming, and composable error handling.

The Problem with Exceptions

// Exception-based approach - problems!
function parseUser(json: string): User {
    try {
        const data = JSON.parse(json);  // Might throw
        
        if (!data.username) {
            throw new Error('Missing username');
        }
        
        if (!data.email) {
            throw new Error('Missing email');
        }
        
        return {
            username: data.username,
            email: data.email
        };
    } catch (error) {
        // Lost type information
        throw new Error(`Parse error: ${error}`);
    }
}

function processUser(json: string): void {
    try {
        const user = parseUser(json);  // Might throw, but type doesn't say so!
        saveUser(user);  // Might throw too!
        sendEmail(user.email);  // And this!
    } catch (error) {
        // Which function threw? What error type?
        console.error('Something failed:', error);
    }
}

Problems:

  • Errors not visible in type signatures

  • Lost type information in catch blocks

  • Breaks function composition

  • Difficult to handle multiple error types

  • Hard to track error flow

Either/Result Type

Either represents success (Right) or failure (Left).

Option/Maybe Type

Option represents presence (Some) or absence (None) of a value.

Mapping Over Either

FlatMap/Chain for Either

Real Example: User Registration

Railway-Oriented Programming

Think of function composition as railway tracks. Success path on one track, failure path on another.

Real Example: API Request Processing

Option Helpers

Real Example: Database Query

Combining Multiple Results

Best Practices

1. Make Errors Explicit in Types

2. Use Option for Nullable Values

3. Compose Error Handling

What's Next

You now understand:

  • Either/Result types for explicit errors

  • Option/Maybe for nullable values

  • Railway-oriented programming

  • Composing error handling

  • Type-safe error flows

In Part 5: Advanced Functional Patterns, you'll learn about functors and monads, algebraic data types, functional reactive programming, and advanced patterns for production systems.


Based on building production TypeScript APIs with type-safe error handling, from user registration to complex request processing pipelines.

Last updated