setup
Environment-based activation
Never enable slowdep in production. Use environment variables to control it per environment. Add to your .env.development and .env.test:
# .env.development SLOWDEP_ENABLED=true # .env.production — omit this entirely # SLOWDEP_ENABLED is unset, so slowdep is bypassed
// lib/slowdep.ts — shared conditional wrapper import { withLatency } from 'slowdep'; import type { LatencyProfile } from 'slowdep'; const enabled = process.env.SLOWDEP_ENABLED === 'true'; export function maybeSlowDep<T extends (...args: any[]) => Promise<any>>( fn: T, profile: LatencyProfile ): T { return enabled ? withLatency(fn, profile) : fn; }
app router
Server Components and API routes
// app/users/page.tsx import { maybeSlowDep } from '@/lib/slowdep'; import { getUsers } from '@/lib/db'; // Wrap at module level — same wrapped function used every render const slowGetUsers = maybeSlowDep(getUsers, 'postgres'); export default async function UsersPage() { const users = await slowGetUsers(); // realistic latency in dev return <UserList users={users} />; } // Suspense loading.tsx shows while data fetches: // app/users/loading.tsx export default function Loading() { return <UserListSkeleton />; }
// app/api/users/route.ts import { NextResponse } from 'next/server'; import { maybeSlowDep } from '@/lib/slowdep'; import { db } from '@/lib/db'; const slowQuery = maybeSlowDep( (sql: string) => db.query(sql), 'postgres' ); export async function GET() { try { const { rows } = await slowQuery('SELECT * FROM users'); return NextResponse.json(rows); } catch (err) { return NextResponse.json({ error: 'Query failed' }, { status: 500 }); } }
pages router
getServerSideProps and API routes
// pages/api/users.ts import type { NextApiRequest, NextApiResponse } from 'next'; import { maybeSlowDep } from '@/lib/slowdep'; import { db } from '@/lib/db'; const slowQuery = maybeSlowDep(db.query.bind(db), 'postgres'); export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { rows } = await slowQuery('SELECT * FROM users'); res.json(rows); } // pages/users.tsx export async function getServerSideProps() { const slowGetUsers = maybeSlowDep(getUsers, 'postgres'); const users = await slowGetUsers(); return { props: { users } }; }
testing
Testing with Next.js test utilities
When testing API routes with next-test-api-route-handler or similar, enable slowdep via the environment variable and assert that your error handling and timeouts work:
import { testApiHandler } from 'next-test-api-route-handler'; import handler from '@/pages/api/users'; describe('GET /api/users with slowdep', () => { beforeAll(() => { process.env.SLOWDEP_ENABLED = 'true'; }); afterAll(() => { delete process.env.SLOWDEP_ENABLED; }); it('returns users despite realistic latency', async () => { await testApiHandler({ handler, async test({ fetch }) { const res = await fetch({ method: 'GET' }); expect(res.status).toBe(200); const data = await res.json(); expect(Array.isArray(data)).toBe(true); }, }); }, 10000); // extend jest timeout to allow for p99 latency });
Jest timeout
When using slowdep in tests, extend the Jest test timeout to accommodate p99 latency. Set jest.setTimeout(10000) globally or pass a timeout per test.
what to test
Scenarios this integration covers
- Suspense loading boundaries — slow Server Component data fetching lets you verify
loading.tsxrenders while data loads - Error.tsx boundaries — combined with errorRate, test that your error UI renders on database failures
- Streaming SSR — verify React streaming works correctly when some components are slower than others
- API route timeouts — test that your route returns a 504 (or a fallback) rather than hanging indefinitely
- Client-side loading states — slow API routes keep client fetch in-flight, letting you assert loading spinners and skeleton UIs
- ISR revalidation — slow database calls in revalidation paths; verify stale content is served while revalidation completes