Four steps to realistic latency
npm install slowdep
withLatency using ESM or CommonJS — both work out of the box.import { withLatency } from 'slowdep'
const { withLatency } = require('slowdep')
// Your original async function async function queryUser(id) { return db.query('SELECT * FROM users WHERE id = $1', [id]) } // Wrapped with Postgres-realistic latency const slowQueryUser = withLatency(queryUser, 'postgres')
// Use exactly like the original const user = await slowQueryUser(42) console.log(user.name) // same result, realistic timing
What just happened?
When you call slowQueryUser(42), slowdep samples a latency value from a lognormal distribution parameterized by the 'postgres' preset (p50: 5ms, p99: 200ms). It waits that many milliseconds, then calls your original queryUser(42) function and returns its result.
The lognormal distribution is key. Unlike a flat setTimeout, it produces realistic variance: most calls are fast (near 5ms), some take longer (20–50ms), and occasionally one takes 100–200ms. That long tail is where timeout misconfiguration and missing retry logic hide.
If the preset's errorRate fires (postgres: 0.1% of calls), the wrapper rejects with a transient error before calling your function — giving your retry and fallback code something real to handle.
Only wrap in development and test environments. Use process.env.NODE_ENV !== 'production' ? withLatency(fn, preset) : fn to ensure zero overhead in production. See Production usage for the full pattern.