---
title: "22 — Testing and the Custom Server Escape Hatch"
uid: testing-custom-server
tags: ["roadmap:nextjs", "vitest", "jest", "playwright", "testing", "custom-server", "cypress", "nextjs"]
excerpt: "Testing splits by scope — Vitest/Jest for units, Playwright/Cypress for end-to-end — and the custom server is an escape hatch, not a default."
date: 2026-08-13T03:27:58+0000
source: https://www.aveshina.my.id/en/blog/testing-custom-server
---

Testing a Next.js app looked like one question until I noticed it was really two, split by scope. The model that clicked: **testing splits by scope — Vitest or Jest for unit/integration tests at the base, Playwright or Cypress for end-to-end tests at the top — and the custom server is an escape hatch for programmatic server patterns, not the default** [1][2][3][4][5][6]. The framework's own server (next start) handles the vast majority of cases.

## The testing landscape

Next.js supports the major testing frameworks, and the choice is by scope:

- **Vitest** — a fast unit test framework powered by Vite, with HMR (hot module replacement — code updates without a full reload), TypeScript, and JSX support built in [1]. Good fit for projects already on Vite-style tooling.
- **Jest** — a complete JavaScript testing framework with a runner, assertions, mocking, and coverage, popular for React apps [2]. The long-standing default for unit and snapshot testing.
- **Playwright** — automates Chromium, Firefox, and WebKit with one API, used for end-to-end testing [3].
- **Cypress** — supports both end-to-end and component testing, with visual debugging [4].

```figure
<svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Testing pyramid for Next.js. Base: many unit tests (Vitest / Jest). Middle: integration tests. Top: few end-to-end tests (Playwright / Cypress). Beside it: a locked door labeled 'custom server — escape hatch'.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- pyramid -->
    <polygon points="80,240 280,240 230,180 130,180" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="180" y="220" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">unit tests</text>
    <text x="180" y="234" font-size="10" fill="#475569" text-anchor="middle">Vitest · Jest</text>

    <polygon points="130,180 230,180 205,130 155,130" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="180" y="160" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">integration</text>

    <polygon points="155,130 205,130 180,80" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="180" y="108" font-size="10" font-weight="700" fill="#7f1d1d" text-anchor="middle">E2E</text>
    <text x="180" y="72" font-size="10" fill="#475569" text-anchor="middle">Playwright · Cypress</text>

    <!-- escape hatch -->
    <rect x="380" y="100" width="280" height="140" rx="10" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="520" y="124" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">custom server</text>
    <rect x="500" y="140" width="40" height="60" rx="4" fill="#1e1b4b" stroke="#6366f1"/>
    <circle cx="520" cy="172" r="4" fill="#e0e7ff"/>
    <text x="520" y="222" font-size="10" font-style="italic" fill="#1e1b4b" text-anchor="middle">escape hatch — rarely needed</text>

    <text x="360" y="270" font-size="11" fill="#64748b" text-anchor="middle" font-style="italic">test by scope, reach for custom server only when you must</text>
  </g>
</svg>
```

## Unit and integration testing

Vitest and Jest cover the base of the pyramid — testing individual functions, hooks, and components in isolation [1][2]. Both have first-class Next.js setup guides. The choice between them is largely taste and ecosystem: Vitest if the project values Vite-aligned speed and DX (developer experience), Jest if it wants the mature, everything-included default.

## End-to-end testing

Playwright and Cypress drive a real browser against the running app [3][4]. They test the full stack — routing, data fetching, rendering, interaction — as a user experiences it. Playwright's strength is cross-browser coverage with one API; Cypress's is the visual debug loop and component testing support.

The discipline with E2E: keep the suite small. E2E tests are slow and flaky-prone compared to units. Reserve them for the critical user journeys (login, checkout, the main flow), and let unit tests cover the rest.

## The custom server — escape hatch, not default

Next.js includes its own server via next start [5]. If there's an existing backend, I can still use it alongside Next.js without a custom server. A **custom server** lets me programmatically start a server for custom patterns — and the roadmap is explicit: the majority of the time, I will not need this approach [5]. It's available when I need to eject.

The reasons to reach for a custom server are narrow: integrating Next.js into a larger existing Node server with custom routing, running server-side logic that doesn't fit the route handler model, or specific hosting constraints. The cost is real — a custom server opts out of some of Next.js's automatic optimizations and complicates deployment. The default next start supports all Next.js features and is what production deployments use.

## How I use this

For testing, I split by scope: Vitest (or Jest) for units and component tests, Playwright for a small set of E2E tests covering the critical flows. I resist the urge to E2E everything — slow, flaky suites are worse than no tests. For the server, I've never needed a custom server in practice; next start covers every deployment I've done, and route handlers cover any server-side logic. The custom server stays in the toolbox as a labeled escape hatch I expect to rarely open.

## References

[1] Vercel, "How to set up Vitest with Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/testing/vitest](https://nextjs.org/docs/app/guides/testing/vitest)

[2] Vercel, "How to set up Jest with Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/testing/jest](https://nextjs.org/docs/app/guides/testing/jest)

[3] Vercel, "How to set up Playwright with Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/testing/playwright](https://nextjs.org/docs/app/guides/testing/playwright)

[4] Vercel, "How to set up Cypress with Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/testing/cypress](https://nextjs.org/docs/app/guides/testing/cypress)

[5] Vercel, "How to set up a custom server in Next.js," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/guides/custom-server](https://nextjs.org/docs/app/guides/custom-server)

[6] Vitest, "Vitest — A blazing-fast unit-test framework powered by Vite," vitest.dev, 2024. [Online]. Available: [https://vitest.dev/](https://vitest.dev/)

[7] Playwright, "Playwright — Fast and reliable end-to-end testing," playwright.dev, 2024. [Online]. Available: [https://playwright.dev/](https://playwright.dev/)

```quiz
Q: Which testing tools cover the unit/integration layer of the pyramid?
- Playwright and Cypress
- Vitest and Jest
correct: 1
explain: Vitest and Jest are unit/integration frameworks — fast, isolated, run against functions and components. Playwright and Cypress drive a real browser for E2E.

Q: What's a good discipline for end-to-end tests?
- Write an E2E test for every component
- Keep the suite small — reserve E2E for critical user journeys, cover the rest with unit tests
correct: 1
explain: E2E tests are slow and flaky-prone. A small suite focused on critical flows gives the most signal per cost; units cover the rest.

Q: When do you need a custom server in Next.js?
- Whenever you have an existing backend
- Rarely — for narrow cases like integrating into a larger existing Node server; the default next start supports all features
correct: 1
explain: The roadmap is explicit: most projects don't need a custom server. Having an existing backend doesn't require it. It's an escape hatch for unusual programmatic patterns, at the cost of opting out of some optimizations.

Q: Playwright's distinguishing strength is…
- visual debugging only available in Chromium
- cross-browser automation (Chromium, Firefox, WebKit) with a single API
correct: 1
explain: Playwright automates all three major browser engines with one API, making it the choice when cross-browser E2E coverage matters.

Q: A custom server opts out of some of Next.js's automatic optimizations. True or false?
- True
- False — it's fully equivalent to next start
correct: 0
explain: A custom server complicates deployment and opts out of some automatic optimizations. That's why it's an escape hatch rather than the default.
```
