Wrap any async function with realistic latency.
Test how your app survives slow, unpredictable dependencies.
Drop slowdep anywhere you call an external dependency. It preserves the original function signature — same inputs, same outputs, same errors. Just slower and more realistic.
import { withLatency } from 'slowdep'; // your real function, unchanged const findUser = async (id) => db.query('SELECT * FROM users WHERE id = $1', [id]); // wrap it — now behaves like real Postgres const slowFindUser = withLatency(findUser, 'postgres'); const user = await slowFindUser(42); // every call is different
import { withLatency } from 'slowdep'; const slowFetch = withLatency(fetchExternalAPI, { p50: 100, // typical call: ~100ms p99: 2000, // worst 1%: up to 2 seconds errorRate: 0.01, // 1% throw a transient error }); const data = await slowFetch('/api/users');
import { withLatencyAll } from 'slowdep'; // wrap every method on a client at once const slowRedis = withLatencyAll(redisClient, 'redis'); await slowRedis.get('key'); // slow await slowRedis.set('k', 'v'); // slow await slowRedis.del('key'); // slow
Based on real p50/p95/p99 data from production systems. Pass a string and you're done.
A flat delay gives every call the same wait time. Real dependencies don't work like that — they respond in 5ms most of the time, hit 200ms occasionally, and spike past 1s when GC pauses, cold caches, or noisy neighbors show up.
slowdep fits a lognormal distribution to your p50 and p99 values using the Box-Muller transform. The result: fast most of the time, with a realistic long tail — the exact shape that breaks retry logic, timeouts, and circuit breakers in production.