Core API
safe.all
Runs multiple safe-wrapped async operations in parallel and returns all values as a named object, or the first error encountered.
Signature
safe.all<T extends Record<string, Promise<SafeResult<any, any>>>>(
promises: T
): Promise<SafeResult<
{ [K in keyof T]: T[K] extends Promise<SafeResult<infer V, any>> ? V : never },
T[keyof T] extends Promise<SafeResult<any, infer E>> ? E : never
>>
Accepts an object map of Promise<SafeResult> entries. The keys you choose become the named properties on the result.
- On all-success: returns
ok({ key: value, ... })with unwrapped values - On any failure: returns
err(firstError)
Basic usage
const [data, error] = await safe.all({
user: safe.async(() => fetchUser(userId)),
posts: safe.async(() => fetchPosts(userId)),
})
if (error) {
console.error('Something failed:', error.message)
return
}
data.user // User
data.posts // Post[]
Error handling
If any operation fails, safe.all short-circuits and returns the first error. The remaining results are discarded.
const [data, error] = await safe.all({
user: safe.async(() => fetchUser(userId)),
posts: safe.async(() => Promise.reject(new Error('posts failed'))),
})
if (error) {
console.error(error.message) // "posts failed"
}
First error wins
All promises run in parallel via Promise.all. If multiple operations fail, the error from the first key (in object key order) that failed is returned.
With per-operation error mapping
Each operation can have its own parseError, hooks, retry, and timeout configuration — they are fully independent safe.async calls.
const [data, error] = await safe.all({
config: safe.async(() => loadConfig()),
user: safe.async(
() => fetchUser(userId),
(e) => ({ code: 'USER_ERROR', message: String(e) })
),
})
With createSafe
On a createSafe instance, all accepts raw async functions instead of pre-wrapped Promise<SafeResult> entries. The instance applies its own parseError, hooks, retry, and timeout to each function automatically.
const appSafe = createSafe({
parseError: (e) => ({
code: 'ERR',
message: e instanceof Error ? e.message : 'Unknown',
}),
defaultError: { code: 'ERR', message: 'Unknown' },
})
const [data, error] = await appSafe.all({
user: () => fetchUser(userId),
posts: () => fetchPosts(userId),
})
if (error) {
console.error(error.code) // typed as { code: string; message: string }
}
Standalone vs createSafe
The standalone safe.all accepts Promise<SafeResult> entries (e.g., safe.async(() => ...)). The createSafe instance all accepts raw async functions and wraps them automatically with the instance's configuration.
Tagged result properties
The result from safe.all is a full SafeResult with tagged properties:
const result = await safe.all({
user: safe.async(() => fetchUser(userId)),
posts: safe.async(() => fetchPosts(userId)),
})
if (result.ok) {
result.value.user // User
result.value.posts // Post[]
}
// Or use destructuring
const [data, error] = result
Empty input
Passing an empty object returns ok({}):
const [data, error] = await safe.all({})
// data = {}, error = null
When to use safe.all vs safe.allSettled
Use safe.all when all operations must succeed for the result to be useful (e.g., loading a page that requires both user data and permissions). Use safe.allSettled when you want to handle each result independently (e.g., loading optional sidebar widgets).