level 05 / rest-api-testing

REST API Testing with Playwright

Use Playwright's request fixture to test REST APIs directly — no browser required.

practice site: Restful Booker

Why API Testing Matters

API tests run 10–100× faster than UI tests and catch integration bugs at the contract boundary. Playwright’s request fixture makes REST testing first-class — same runner, same config, same CI pipeline.

The test pyramid puts API tests above unit tests but below UI tests. Shift coverage down: every user flow that touches an API should have an API-layer test, not just a UI test.

The request Fixture

Playwright exposes APIRequestContext as the request fixture. It handles cookies, base URL, and response assertions natively.

import { test, expect } from '@playwright/test';

test('GET /bookings returns list', async ({ request }) => {
  const res = await request.get('https://restful-booker.herokuapp.com/booking');
  expect(res.status()).toBe(200);
  const body = await res.json();
  expect(Array.isArray(body)).toBe(true);
});

CRUD Flow

test('full booking CRUD', async ({ request }) => {
  // Create
  const create = await request.post('/booking', {
    data: {
      firstname: 'Jane',
      lastname: 'Doe',
      totalprice: 150,
      depositpaid: true,
      bookingdates: { checkin: '2025-01-01', checkout: '2025-01-07' },
    },
  });
  expect(create.status()).toBe(200);
  const { bookingid } = await create.json();

  // Read
  const get = await request.get(`/booking/${bookingid}`);
  expect(get.status()).toBe(200);
  expect(await get.json()).toMatchObject({ firstname: 'Jane' });

  // Update (requires auth token)
  const token = await getToken(request);
  const patch = await request.patch(`/booking/${bookingid}`, {
    headers: { Cookie: `token=${token}` },
    data: { firstname: 'Updated' },
  });
  expect(patch.status()).toBe(200);

  // Delete
  const del = await request.delete(`/booking/${bookingid}`, {
    headers: { Cookie: `token=${token}` },
  });
  expect(del.status()).toBe(201);
});

async function getToken(request: any) {
  const res = await request.post('/auth', {
    data: { username: 'admin', password: 'password123' },
  });
  return (await res.json()).token;
}

APIRequestContext vs. fetch

Featurerequest fixturefetch
Base URL from config❌ manual
Cookie jar
expect(res).toBeOK()
Works in beforeAll
TypeScript response typesvia .json()via generics

Response Assertions

// Status helpers
expect(res.ok()).toBe(true);           // 200–299
expect(res.status()).toBe(200);
expect(res.statusText()).toBe('OK');

// Body
const json = await res.json();
expect(json).toMatchObject({ id: expect.any(Number) });

// Headers
expect(res.headers()['content-type']).toContain('application/json');

Practice: Restful Booker

EndpointMethodAuth required
/bookingGETNo
/bookingPOSTNo
/booking/:idPUT/PATCHYes (token)
/booking/:idDELETEYes (token)
/authPOST— (generates token)