DEV Community

Michael Lip
Michael Lip

Posted on • Originally published at theluckystrike.github.io

I Test All My APIs in Chrome DevTools. Here's How.

Chrome ships with everything you need to test REST APIs without installing a separate client. Full disclosure: I built JSON Formatter Pro as part of Zovo, a collection of 16 Chrome extensions I maintain. Take my perspective accordingly.

I run API tests in DevTools dozens of times a day. The Console gives you a full JavaScript REPL for firing HTTP requests. The Network panel captures every call with header inspection, timing breakdowns, and response previews. And because fetch() runs in your page's context, you test against live cookies, tokens, and session state without copy-pasting anything. This guide covers the native methods I actually use, from basic fetch() calls to response mocking and performance profiling. I wrote a more detailed version with additional examples if you want to go deeper.

How Chrome Handles API Requests

Every HTTP request flows through Chrome's multi-process architecture. When your JavaScript calls fetch(), the request starts in the renderer process, crosses into the Network Service (a dedicated process since Chrome 67), and hits the OS network stack.

The Network Service handles DNS resolution, connection pooling, TLS negotiation, and HTTP/2 multiplexing. Chrome maintains up to 6 TCP connections per origin under HTTP/1.1 and a single multiplexed connection under HTTP/2 or HTTP/3. When you see a request "stalled" in the Network panel, it usually means the browser hit that per-origin connection limit and is waiting for a slot.

DevTools hooks into all of this through the Chrome DevTools Protocol (CDP), a WebSocket-based protocol exposing domains like Network, Runtime, and Fetch. Every request in the Network panel is a CDP event rendered in the UI. This same protocol powers Puppeteer and Playwright, which is why browser automation captures the exact same network data you see in DevTools.

For deeper troubleshooting, chrome://net-internals exposes the raw event log of every network operation, including socket reuse decisions, proxy resolution, and QUIC session state.

Making Your First API Call

Open DevTools with Cmd+Option+I (Mac) or Ctrl+Shift+I (Windows), then click the Console tab. The Console runs in the context of the current page, so any fetch() call shares the page's cookies, origin, and session state.

Here's a simple GET request:

const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const data = await response.json();
console.log(data);

Chrome's Console supports top-level await, so no async wrapper needed. The response object gives you status, headers, and methods like json(), text(), and blob().

POST requests work the same way, with a second argument for method, headers, and body:

const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Test', body: 'Content', userId: 1 })
});
const data = await response.json();
console.log(data);

The Console preserves command history. Press the up arrow to recall previous commands, which saves real time when you're iterating on a request.

Inspecting Live Traffic in the Network Panel

Switch to the Network tab to see every HTTP request the page makes in real time. Click the Fetch/XHR filter to narrow the list to API calls, which immediately hides the noise.

Click any request to open a detail pane. Headers shows request and response headers, including auth tokens and CORS details. Payload shows the request body for POST and PUT. Preview renders JSON responses as a collapsible tree. Response shows the raw text.

Press Cmd+F (Mac) or Ctrl+F (Windows) with the Network panel focused to search across all captured URLs, headers, and response bodies simultaneously. The filter bar also accepts regex when you prefix with /, and supports property filters like status-code:404 or method:POST.

Replaying and Modifying Requests

Right-click any request in the Network panel for replay options. "Copy as fetch" generates a complete fetch() call with all original headers, ready to paste into the Console, tweak, and run. "Copy as cURL" gives you the terminal equivalent.

For XHR requests specifically, "Replay XHR" resends the exact same request. For Fetch API calls, copy-paste to the Console is your best option.

The Sources panel also has a Snippets feature that acts as a lightweight notebook. Create a snippet, write your API test code, and run it anytime with Cmd+Enter (Mac) or Ctrl+Enter (Windows). Snippets persist across browser sessions, making them a practical alternative to external API clients for repeatable tests.

Working with Authentication

fetch() sends the page's cookies by default for same-origin requests. If the API shares the same domain as the page, authentication just works.

For cross-origin requests that need cookies, add credentials: 'include' to your fetch options:

const response = await fetch('https://api.example.com/protected', {
credentials: 'include',
headers: { 'Authorization': 'Bearer YOUR_TOKEN_HERE' }
});

Store tokens in Console variables and reuse them across multiple requests. The variable persists until you navigate away or close DevTools. Assign tokens once at the top of your session and reference them by name in every subsequent call.

Advanced Techniques

Local Overrides for Response Mocking

Chrome's Local Overrides let you intercept API responses and replace them with local files. Go to Sources, click the Overrides tab, select a local folder, then right-click any API response in the Network panel and choose "Override content."

Chrome saves the response locally and serves your version on subsequent requests. The overrides persist across reloads. In my experience, this replaces the need for mock servers in about 70% of frontend debugging scenarios.

Network Request Blocking

Block specific endpoints to test failure handling. Right-click any request and choose "Block request URL," or open the Network request blocking pane. URL patterns support wildcards, so api.example.com/users blocks all user-related endpoints at once.

When a blocked request fires, Chrome returns a network error. This lets you verify error states, fallback UI, and retry logic without touching the backend.

Throttling

The Network panel's throttling dropdown simulates different connection speeds. Built-in presets include Fast 3G (1.6 Mbps down, 562ms RTT) and Slow 3G (500 Kbps down, 2000ms RTT). You can also create custom profiles. The "Offline" preset cuts all network access, which is useful for testing offline-capable apps.

Measuring Performance

Click any request in the Network panel, then open the Timing tab to see where time goes: DNS Lookup, Initial Connection, SSL, Request Sent, Waiting (TTFB), and Content Download.

For a typical HTTPS call, DNS runs under 5ms for cached domains. TCP adds 10 to 50ms depending on distance. TLS 1.3 adds another 10 to 30ms. TTFB under 200ms means a responsive server, while anything above 500ms suggests backend bottlenecks worth investigating.

For scriptable benchmarks, wrap calls with performance.now():

const t0 = performance.now();
const res = await fetch('https://api.example.com/data');
const json = await res.json();
console.log(Request + parse: ${(performance.now() - t0).toFixed(1)}ms);

The Resource Timing API gives you programmatic access to the same data. Calling performance.getEntriesByType('resource') returns every loaded resource with sub-millisecond precision on each phase, useful for regression tracking.

Common Problems

CORS Errors

When you call fetch() from the Console targeting a different origin, Chrome blocks the response unless the server includes the correct Access-Control-Allow-Origin header. If you control the server, add the headers. If not, launch Chrome with --disable-web-security from a dedicated testing profile. Never use that flag for general browsing.

"(failed)" with No Details

This usually means the request never reached the server. Common causes include an ad blocker intercepting it, a CSP directive blocking the URL, or the server being unreachable. Disable extensions one by one to isolate the cause, and check the Console for CSP violation messages.

JSON Displays as Raw Text

The Network panel only activates JSON Preview when Content-Type is application/json. Servers sending text/plain cause Chrome to treat it as plain text. Copy the raw response and run JSON.parse() in the Console as a workaround. For frequent JSON work, JSON Formatter Pro detects and formats JSON regardless of Content-Type.

Cookies Not Sent with fetch()

fetch() includes cookies for same-origin requests by default but omits them cross-origin. Add credentials: 'include' to your options, and make sure the server responds with Access-Control-Allow-Credentials: true and a specific origin (the wildcard * does not work with credentials).

Request Appears Twice

You're seeing a CORS preflight. When fetch() uses custom headers or non-simple methods like PUT or DELETE, Chrome sends an OPTIONS request first to check server permissions. Both appear in the Network panel. Filter by the Method column to distinguish them.

Extensions Worth a Look

Chrome's built-in tools cover most API testing needs, but a few extensions smooth out the daily workflow. JSON Formatter Pro formats raw JSON responses directly in the browser tab with collapsible trees and syntax highlighting, so you can hit an API endpoint URL and read the response without opening DevTools. JSONView is an older alternative that handles basic formatting reliably. ModHeader lets you add or modify HTTP headers at the browser level, which is useful for injecting feature flags or overriding Content-Type without writing code.

For most solo developers, a JSON formatter plus DevTools handles the majority of API testing. Dedicated clients like Postman earn their place when you need shared collections, environment variables, or automated test suites.

I build Chrome extensions at zovo.one. All 16 are free, open source, and collect zero data.

Top comments (0)