Your delete test needs a contact to exist. Creating it through the form takes eight seconds. Multiply by forty tests and your suite spends most of its life filling in forms it is not testing.
Playwright has a full HTTP client built in, so you do not need a second tool.
test('creating a contact through the API', async ({ request }) => {
const response = await request.post('/api/contacts', {
data: { name: 'Dana Fox', company: 'FoxLabs' },
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body.name).toBe('Dana Fox');
});
request is a fixture, like page. Ask for it and you get an HTTP client that already knows your baseURL.
Two distinct uses, and it is worth keeping them separate in your head.
Use one: fast setup. The contact exists so you can test deleting it:
test.beforeEach(async ({ request, page }) => {
await request.post('/api/contacts', {
data: { name: `Dana ${Date.now()}`, company: 'FoxLabs' },
});
await page.goto('/contacts');
});
A few hundred milliseconds instead of eight seconds, and it cannot fail because a form field moved.
Use two: testing the API itself. Here the request is the subject:
test('rejects a contact with no name', async ({ request }) => {
const response = await request.post('/api/contacts', { data: { name: '' } });
expect(response.status()).toBe(400);
expect(await response.json()).toMatchObject({ error: /name is required/ });
});
API tests are fast and stable because no screen is involved. Many teams cover most validation rules here and keep only the main journeys in the browser.
Three things people get wrong.
request is not the same as intercepting. request makes a new call from your test. Route interception watches or changes the calls the app makes. Different jobs, and interviewers like the distinction.
Authentication does not happen by magic. The request fixture does not share the browser's cookies unless you set that up. For an authenticated API call you usually send a token, or create a context with saved state.
Do not verify only through the API. If a test creates a contact by API and checks it by API, it has tested the server twice and the app not at all. Create by API, verify on screen.
Remember this: arrange through the API because it is fast, assert through the screen because that is what users see.
Reference: API testing.