Signature

TypeScript signature

function withLatencyAll<T extends object>(
  client: T,
  profile: LatencyPreset | LatencyOptions
): T

The return type is the same as the input type — a new object with the same shape as client, but with every async method wrapped. Non-async methods are passed through unchanged.

Parameters

Function parameters

paramtypedefaultdescription
client object Any JavaScript object. All methods that return a Promise will be wrapped. The original object is not mutated — a new object with wrapped methods is returned.
profile LatencyPreset | LatencyOptions The same latency profile is applied to all wrapped methods. Pass a preset string or a custom LatencyOptions object.
Return value

What withLatencyAll returns

withLatencyAll returns a new object (not a Proxy) with the same prototype chain as the original client. Each method is inspected at wrap time: if calling it with no arguments returns a Promise (or if it is detected as an async function), it is wrapped with withLatency. Synchronous methods are passed through unchanged.

this binding is preserved — each wrapped method is bound to the original client object, so class methods that reference this.someProperty or this.db continue to work correctly.

Method detection

Which methods get wrapped

At wrap time, withLatencyAll enumerates the own and inherited enumerable methods on the object. A method is wrapped if it is a function and either:

  • It is declared with the async keyword (detected via fn.constructor.name === 'AsyncFunction').
  • It explicitly returns a Promise instance.

Synchronous methods — getters, event emitters, configuration accessors — are included on the returned object unchanged. If a method is conditionally async, it will only be wrapped if it is flagged as an async function at the time withLatencyAll is called.

Examples

Code examples

import { createClient } from 'redis'
import { withLatencyAll } from 'slowdep'

const redis = createClient()
await redis.connect()

// Wrap every async method — get, set, del, hGet, etc.
const slowRedis = withLatencyAll(redis, 'redis')

// Use exactly like the original client
await slowRedis.set('key', 'value')
const val = await slowRedis.get('key')
import { Pool } from 'pg'
import { withLatencyAll } from 'slowdep'

const pool = new Pool({ connectionString: process.env.DATABASE_URL })

// Wrap the pool — query, connect, end all get latency
const slowPool = withLatencyAll(pool, 'postgres')

const { rows } = await slowPool.query(
  'SELECT * FROM users WHERE active = $1',
  [true]
)
import { withLatencyAll } from 'slowdep'

class PaymentService {
  async createCharge(amount: number) { /* ... */ }
  async refund(chargeId: string) { /* ... */ }
  formatAmount(cents: number) { return cents / 100 } // sync — not wrapped
}

const payments = new PaymentService()

// createCharge and refund get Stripe latency; formatAmount is unchanged
const slowPayments = withLatencyAll(payments, 'stripe')

const charge = await slowPayments.createCharge(1000)
const label = slowPayments.formatAmount(1000) // still synchronous
Caveats

Things to be aware of

Synchronous methods pass through unchanged

Only methods that return a Promise are wrapped. Synchronous methods — property accessors, formatters, event emitters — appear on the returned object as-is. Calling them will not add latency.

this binding is preserved

Each wrapped method is bound to the original client instance. You do not need to call .bind() manually. This means class methods that access this.db, this.config, or other instance properties will work correctly through the wrapped object.

Same profile for all methods

withLatencyAll applies one profile to every async method on the client. If you need different latency for different methods (e.g., reads vs writes), use withLatency to wrap each method individually with its own profile.

Don't wrap in production

Like withLatency, withLatencyAll should only be used in development and test environments. Use an environment check: process.env.NODE_ENV !== 'production' ? withLatencyAll(client, preset) : client.