ReqRes Blog

API Testing with Playwright - Real Endpoints vs Mocking

Feb 6, 2026 4 min read
playwright api-testing qa automation e2e
Next step

Want to try this in 2 minutes? Start a project or open the Notes example.

Playwright has excellent built-in API mocking with page.route(). But mocking every endpoint means your tests only prove your mocks work - not your integration. This guide shows how to use ReqRes as a real test backend alongside Playwright, so you can test auth flows, data persistence, and real HTTP behaviour end-to-end.

When should you mock vs use a real API?

This isn't an either/or decision. Most teams should use both approaches strategically.

Use mocking when... Use a real API when...
Testing UI rendering with specific data shapes Testing actual HTTP integration
Simulating error states (500, 429, timeouts) Testing authentication flows end-to-end
Speed is critical and network calls add latency Verifying real status codes and response headers
Backend doesn't exist yet Testing pagination, filtering, CRUD
You need deterministic data for visual regression You need confidence the integration actually works

The key insight: mock for UI-specific tests, use a real API for integration and E2E tests. ReqRes gives you the speed of a mock (hosted, fast, no setup) with the confidence of a real backend.

How do I set up Playwright with ReqRes?

Setting up takes about 2 minutes:

1. Get a free API key

Create your key at https://app.reqres.in/?next=/api-keys

2. Store it in your environment

Add to .env:

REQRES_API_KEY=your_key_here

3. Configure Playwright

Update playwright.config.ts:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'https://reqres.in',
    extraHTTPHeaders: {
      'x-api-key': process.env.REQRES_API_KEY || '',
    },
  },
});

Now every request automatically includes your API key.

How do I write API tests with Playwright and ReqRes?

Playwright's request fixture makes API testing straightforward. Here are practical examples:

GET request with assertions:

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

test('GET /api/users returns paginated users', async ({ request }) => {
  const response = await request.get('/api/users?page=2');
  expect(response.status()).toBe(200);

  const body = await response.json();
  expect(body.data).toBeTruthy();
  expect(body.data.length).toBeGreaterThan(0);
  expect(body.page).toBe(2);
});

POST request to create data:

test('POST /api/users creates a user', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: {
      name: 'QA Engineer',
      job: 'Automation Lead',
    },
  });
  expect(response.status()).toBe(201);

  const body = await response.json();
  expect(body.name).toBe('QA Engineer');
  expect(body.id).toBeTruthy();
});

Testing error responses:

test('GET non-existent user returns 404', async ({ request }) => {
  const response = await request.get('/api/users/999999');
  expect(response.status()).toBe(404);
});

How do I test authentication flows in Playwright?

This is where real API testing shines. Auth flows are dangerous to mock - a misconfigured header in a mock still returns 200, hiding bugs that break production.

Testing the full auth flow:

test('authenticated user can access their data', async ({ request }) => {
  // Step 1: Request magic link login
  const loginResponse = await request.post('/api/app-users/login', {
    data: { email: '[email protected]' },
  });
  expect(loginResponse.status()).toBe(200);

  const { session_token } = await loginResponse.json();
  expect(session_token).toBeTruthy();

  // Step 2: Access user-scoped endpoint with Bearer token
  const dataResponse = await request.get('/app/collections/todos/records', {
    headers: {
      'Authorization': `Bearer ${session_token}`,
    },
  });
  expect(dataResponse.status()).toBe(200);
});

Testing that unauthenticated requests fail:

test('unauthenticated request returns 401', async ({ request }) => {
  const response = await request.get('/app/collections/todos/records');
  expect(response.status()).toBe(401);
});

Testing user data isolation:

test('users only see their own data', async ({ request }) => {
  // Login as user A
  const loginA = await request.post('/api/app-users/login', {
    data: { email: '[email protected]' },
  });
  const { session_token: tokenA } = await loginA.json();

  // Create a record as user A
  await request.post('/app/collections/notes/records', {
    headers: { 'Authorization': `Bearer ${tokenA}` },
    data: { title: 'User A note', private: true },
  });

  // Login as user B
  const loginB = await request.post('/api/app-users/login', {
    data: { email: '[email protected]' },
  });
  const { session_token: tokenB } = await loginB.json();

  // User B should not see user A's note
  const notesB = await request.get('/app/collections/notes/records', {
    headers: { 'Authorization': `Bearer ${tokenB}` },
  });
  const body = await notesB.json();
  const userANotes = body.data.filter(n => n.title === 'User A note');
  expect(userANotes.length).toBe(0);
});

These tests catch real auth bugs that mocking would miss entirely.

How do I combine mocking and real API calls in the same test suite?

Use the hybrid approach - it's the best of both worlds.

Organise your tests by type:

/tests
  /ui          # Mocked for speed and determinism
  /api         # Real API for integration confidence
  /e2e         # Full user flows with real backend

Mocking for UI-specific tests:

// tests/ui/user-list.spec.ts
import { test, expect } from '@playwright/test';

test('renders user list correctly', async ({ page }) => {
  // Mock the API response for predictable UI testing
  await page.route('**/api/users*', route => {
    route.fulfill({
      status: 200,
      body: JSON.stringify({
        data: [
          { id: 1, first_name: 'Test', last_name: 'User', avatar: '' },
        ],
      }),
    });
  });

  await page.goto('/users');
  await expect(page.getByText('Test User')).toBeVisible();
});

Real API for integration tests:

// tests/api/users.spec.ts
import { test, expect } from '@playwright/test';

test('API returns valid user data', async ({ request }) => {
  const response = await request.get('/api/users');
  expect(response.status()).toBe(200);

  const body = await response.json();
  expect(body.data[0]).toHaveProperty('first_name');
  expect(body.data[0]).toHaveProperty('email');
});

How do I run Playwright + ReqRes tests in CI?

ReqRes is always available and returns stable payloads, so your CI tests won't flake due to backend instability.

GitHub Actions example:

name: API Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
        env:
          REQRES_API_KEY: ${{ secrets.REQRES_API_KEY }}

Tips for stable CI runs:

  • Add a small delay between requests if running many tests in parallel
  • Use unique email addresses per test run for user isolation tests
  • Check the ReqRes logs dashboard if a test fails unexpectedly

For complete CI/CD setup including GitLab CI, Jenkins, cURL examples, and retry strategies, see our API Testing in CI/CD guide.

Why not just mock everything?

Mocking has its place, but over-mocking creates false confidence.

Mocking hides integration bugs. Your test passes, but production breaks because the real API returns a different structure, different headers, or different status codes than your mock.

Real APIs test the full HTTP stack. DNS resolution, TLS handshakes, request headers, response timing - all the things that can go wrong in production.

Auth flows are especially dangerous to mock. A misconfigured Authorization header in a mock still returns 200. Against a real API, you get 401 immediately and know something's wrong.

ReqRes gives you mock-like speed with real API confidence. It's hosted, fast, requires no setup, and returns consistent responses. You get the developer experience of mocking with the reliability of integration testing.

For more examples you can run right now, see our guide to free APIs for testing.

Where to go next

Get a free API key at https://app.reqres.in/?next=/api-keys and add real API tests to your Playwright suite in under 5 minutes.

For detailed endpoint documentation, check the ReqRes API docs. For a complete overview of QA workflows with ReqRes, see API Testing for QA Engineers.

See a working example app: https://app.reqres.in/?next=/examples/notes-app

Ready to ship? Continue in the app.