skip to content

In Playwright, what does the built-in request fixture let a test do without opening a page?

level: juniorimportance: must knowfreq 72%

answer

  1. A per-test HTTP client fixture
  2. No page, no navigation involved
  3. get, post, put, delete, fetch
  4. Returns an APIResponse you await
  5. Honours baseURL from the config

basics

~20 s

Playwright's request fixture is an APIRequestContext, a per-test HTTP client. It sends get, post, put, patch, delete or fetch calls straight to the server, with no browser page and no navigation, honouring config options such as baseURL.

solid answer

~40 s

The `request` fixture is an `APIRequestContext` built fresh for each test and disposed when the test ends. Destructure it like any fixture — `test('...', async ({ request }) => ...)` — then call `request.get()`, `post()`, `put()`, `patch()`, `delete()`, `head()`, or the generic `request.fetch()`. Each returns an `APIResponse` you inspect with `status()`, `ok()`, `headers()`, `json()`, `text()` or `body()`. Per-call options cover `data` (the body), `form`, `multipart`, `params` (the query string), `headers` and `timeout`. It respects `use` options from the config such as `baseURL` and `extraHTTPHeaders`, so tests can pass paths instead of absolute URLs. Nothing renders: there is no page, no browser cookies and no navigation, which makes it the tool both for pure API tests and for seeding or checking server state around a browser test.

code

typescript · 16 lines
typescript
import { test, expect } from '@playwright/test';

test('the forecast endpoint answers for a saved city', async ({ request }) => {
  const created = await request.post('/api/cities', {
    data: { name: 'Bergen', units: 'metric' },
  });
  await expect(created).toBeOK();

  const forecast = await request.get('/api/forecast', {
    params: { city: 'Bergen', days: 3 },
  });
  await expect(forecast).toBeOK();

  const body = await forecast.json();
  expect(body.days).toHaveLength(3);
});

go deeper

for a junior

Recall that destructuring request in a test hands you an HTTP client, and that every call is awaited and returns a response object you then inspect.

for a middle

Explain that it is an APIRequestContext created and disposed per test, which config use options it inherits, and how data, form, params and headers map onto the wire.

for a senior

Show judgment about which parts of a suite should talk to the server directly, and about making a bad status fail loudly instead of being swallowed by an unasserted response.

for a principal

Own the boundary: how much of the product a suite may exercise over HTTP before it stops proving the shipped user path still works, and who maintains those endpoints.

Playwright's test runner ships a fixture named `request`. Ask for it in a test's argument list and you get an `APIRequestContext` — a complete HTTP client running in the Node process that executes the test. No browser is launched, no page is created, and nothing is rendered. ## What the fixture actually is `APIRequestContext` is the same class Playwright uses wherever it speaks HTTP on your behalf. The runner builds a fresh, isolated instance before each test and disposes it after, so one test cannot leak a cookie or a buffered response into the next. It is **test-scoped and lazy**: name it in the arguments and it is created; ignore it and nothing is built. Because no browser sits in the path, a call costs one round trip rather than a page load. That makes the fixture the natural tool for three jobs: - **Pure API tests**, where a browser never opens at all. - **Preconditions** — creating the rows a browser test needs before it navigates. - **Postconditions** — asking the server what it actually stored after the user clicked something. ## The call surface The verb methods are `get`, `post`, `put`, `patch`, `delete` and `head`, plus `fetch(urlOrRequest, options)`, which takes the method as an option and is what the others delegate to. Every one of them is asynchronous and returns an `APIResponse`, so every call must be awaited. The second argument carries the request: | Option | What it does | |---|---| | `data` | The body. An object is serialised as JSON with a JSON content type; a string or Buffer is sent verbatim. | | `form` | Sends the fields url-encoded instead of as JSON. | | `multipart` | Sends a multipart body, which is how you upload a file. | | `params` | Builds the query string from an object or `URLSearchParams`. | | `headers` | Per-call headers, merged over the context's `extraHTTPHeaders`. | | `timeout` | Per-call deadline in milliseconds; the context default is 30000. | | `failOnStatusCode` | When true, throws instead of returning a response outside 2xx and 3xx. | | `maxRedirects` | How many redirects to follow; `0` returns the 3xx response itself. | In Playwright 1.63 `failOnStatusCode` and `maxRedirects` can also be set once on the context rather than per call. ## Reading the response `APIResponse` exposes `status()`, `statusText()`, `ok()`, `url()`, `headers()`, `headersArray()`, and three body readers — `json()`, `text()` and `body()`. All three are asynchronous. The bodies are buffered and held by the context until it is disposed, which is exactly why the per-test fixture is disposed for you. The default is **permissive**: a 404 or a 500 is not an error, it is a response object. If a test does not assert on the status, a broken endpoint sails through silently. The idiomatic guard is `await expect(response).toBeOK()`. ## Where the configuration comes from The fixture is not configured at the call site alone. It respects the project's `use` block, so a config that sets: - `baseURL` lets calls pass `/api/forecast` rather than a full origin; - `extraHTTPHeaders` attaches an auth token or an `Accept` header to every call; - `httpCredentials`, `ignoreHTTPSErrors` and `proxy` apply the same way. `test.use({ ... })` narrows the same options to one file or `describe` block. ## A weather-dashboard shape For a dashboard backed by a third-party forecast API, a test typically does this: 1. `POST /api/cities` to register the city the test needs. 2. Assert the create call with `await expect(created).toBeOK()`. 3. `GET /api/forecast` with `params: { city, days }` and read `await response.json()`. 4. Let the fixture's automatic disposal clean up when the test ends. ## Lifetime and the usual mistakes The fixture belongs to the test. A context taken in `test.beforeAll` is disposed when that hook finishes, and Playwright reports exactly that if a test later tries to use it — for shared setup, create your own context with `playwright.request.newContext()` and dispose it yourself. The other frequent errors are forgetting an `await` (you then assert on a Promise, which is never a failing status), putting a JSON payload in `params` instead of `data`, and assuming the fixture carries the cookies of a browser context under test. It does not: it is an isolated client whose cookie jar starts empty.

  • Which per-call option puts a JSON body on a Playwright request.post, and which one builds the query string?
    `data` carries the body: pass an object and Playwright serialises it as JSON with a JSON content type, while a string or Buffer is sent as-is. `params` builds the query string from an object or `URLSearchParams`. `form` sends url-encoded fields and `multipart` sends an upload body.
  • Does a Playwright request.get() against an endpoint that returns 500 throw?
    No. By default a response object is returned for every status code, so the test must assert on it — `await expect(response).toBeOK()`, or read `response.status()`. Pass `failOnStatusCode: true`, per call or on the context, if you would rather Playwright throw on anything outside 2xx and 3xx.

saying these in an interview costs you the question

  • Thinks the request fixture drives a hidden browser page
  • Believes it automatically sends the browser context's cookies
  • Calls request.get without awaiting the response
  • Puts a JSON payload in params instead of data
  • Assumes a 404 or 500 response throws by default
  • Keeps a fixture context from beforeAll and reuses it in tests