3.1.1 • Published 6 months ago
promise-for-wrapper v3.1.1
Promise Wrapper
A TypeScript utility library providing robust error handling and data transformation pipelines for asynchronous operations.
Core Utilities
The library provides two main utilities:
promiseFor
: Single-step transformation with built-in error handlingpipeFor
: Multi-step transformation pipeline with automatic error propagation
Installation
npm install promise-wrapper
Error Handling Structure
All operations use a standardized error context:
interface ErrorContext {
message: string;
name: string;
stack?: string | null;
status?: number | null;
url?: string | null;
method?: string | null;
code?: string | null;
context: string;
}
promiseFor: Single-Step Operations
Basic Structure
const [result, error] = await promiseFor(
promise,
postProcessor // optional
);
Simple Example
// Helper function for JSON parsing
const parseJson = async (response) => await response.json();
// Using promiseFor with post-processing
const [data, error] = await promiseFor(
fetch('/api/users'),
parseJson
);
or
const [data, error] = await promiseFor(
fetch('/api/users'),
async (res)=>res.json()
);
if (error) {
console.error('Operation failed:', error.message);
return;
}
pipeFor: Multi-Step Operations
Basic Structure
const [result, error] = await pipeFor(promise)
.transform(transformFn)
.pipe(pipelineFn)
.transform(anotherTransform)
.execute();
Key Concepts
- transform():
- Takes exactly one argument
- Transforms data synchronously or asynchronously
- Automatically handles null/undefined checks
- Returns transformed data to next step
- pipe():
- Introduces new promise-based operations
- Used when you need to make new async calls
- Takes previous result as input
- execute():
- Mandatory final step
- Runs the pipeline
- Returns result, error tuple
Practical Example: Chained API Calls
// Helper functions
const parseJson = response => response.json();
const extractUserId = todo => todo.userId;
const extractUserProfile = user => user.profile;
const formatUserData = profile => ({
name: profile.name,
email: profile.email
});
// Using pipeFor with clean transformations
const [userData, error] = await pipeFor(fetch('/api/todos/1'))
.transform(parseJson) // Parse todo response
.transform(extractUserId) // Get user ID
.pipe(userId => fetch(`/api/users/${userId}`)) // New request with userId
.transform(parseJson) // Parse user response
.transform(extractUserProfile) // Get profile
.transform(formatUserData) // Format final data
.execute(); // Run pipeline
if (error) {
console.error('Failed to fetch user data:', error.message);
return;
}
Automatic Null Checking Example
// Helper functions
const parseJson = response => response.json();
const extractField = data => data.requiredField;
// pipeFor handles null checks automatically
const [result, error] = await pipeFor(fetch('/api/data'))
.transform(parseJson)
.transform(extractField) // No explicit null check needed
.execute();
// If data.requiredField doesn't exist:
// result = null
// error = { message: "Cannot read property 'requiredField' of undefined", ... }
Comparative Examples
Processing Nested Data
Traditional Approach
async function getUserData() {
try {
const todoRes = await fetch('/api/todos/1');
const todo = await todoRes.json();
if (!todo.userId) {
throw new Error('User ID not found');
}
const userRes = await fetch(`/api/users/${todo.userId}`);
const user = await userRes.json();
if (!user.profile) {
throw new Error('User profile not found');
}
return user.profile;
} catch (error) {
console.error(error);
return null;
}
}
Using promiseFor
async function getUserData() {
const [userData, error] = await promiseFor(
fetch('/api/todos/1'),
async (response) => {
const todo = await response.json();
const userRes = await fetch(`/api/users/${todo.userId}`);
const user = await userRes.json();
return user.profile;
}
);
if (error) {
console.error('Failed to get user data:', error);
return null;
}
return userData;
}
Using pipeFor
// Helper functions make the code cleaner and more maintainable
const parseJson = response => response.json();
const extractUserId = todo => todo.userId;
const extractProfile = user => user.profile;
async function getUserData() {
const [profile, error] = await pipeFor(fetch('/api/todos/1'))
.transform(parseJson)
.transform(extractUserId)
.pipe(userId => fetch(`/api/users/${userId}`))
.transform(parseJson)
.transform(extractProfile)
.execute();
if (error) {
console.error('Failed to get user data:', error);
return null;
}
return profile;
}
Best Practices
- Extract Transform Functions
// Helper functions at the top level
const parseJson = response => response.json();
const extractUserId = data => data.userId;
const formatUserData = user => ({
name: user.name,
email: user.email
});
// Clean, readable pipeline
const [result, error] = await pipeFor(fetch('/api/users'))
.transform(parseJson)
.transform(extractUserId)
.transform(formatUserData)
.execute();
- Group Related Operations
const [result, error] = await pipeFor(fetch('/api/users'))
// Data parsing group
.transform(parseJson)
.transform(extractUserData)
// Profile enrichment group
.pipe(user => fetch(`/api/profiles/${user.id}`))
.transform(parseJson)
.transform(enrichUserProfile)
// Final formatting group
.transform(formatResponse)
.execute();
- Keep Transforms Simple
// Don't do this:
.transform(data => {
// Complex logic here
return complexOperation(data).someField;
})
// Do this instead:
const processData = data => complexOperation(data);
const extractField = result => result.someField;
.transform(processData)
.transform(extractField)
- Use Meaningful Names
const [userData, userError] = await pipeFor(fetch('/api/users'))
.transform(parseJson)
.transform(extractUserData)
.execute();
- Handle Errors Appropriately
const [result, error] = await pipeFor(fetch('/api/data'))
.transform(parseJson)
.transform(processData)
.execute();
if (error) {
if (error.status === 404) {
return defaultValue;
}
throw error;
}
Key Reminders
transform
:
- Takes exactly one parameter
- Returns transformed data
- Automatically handles null/undefined
pipe
:
- Use for new promise-based operations
- Takes previous result as input
- Returns new promise
execute
:
- Required as final step
- Returns result, error tuple
- Error handling:
- Automatic null checking in transforms
- Consistent error format
- Pipeline stops at first error
License
MIT License