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.tsx renders 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