Route Handling

Back

Loading concept...

🚦 Network Route Handling in Playwright

The Traffic Controller Story

Imagine you’re building a city with toy cars. Every car (web request) needs to go somewhere—maybe to a toy shop (API server) or a gas station (database).

What if you could be the TRAFFIC CONTROLLER?

You could:

  • STOP certain cars from going anywhere 🛑
  • REDIRECT cars to a different destination 🔄
  • REPLACE what’s inside the car’s delivery truck 📦
  • MODIFY the contents before they arrive ✏️

That’s exactly what Route Handling does in Playwright! You become the traffic controller for all network requests.


🎯 What is Network Interception?

Simple Idea: Catching requests BEFORE they reach their destination.

Think of it like a mail sorting center. Every letter (request) passes through you. You decide:

  • Does this letter go out? ✉️
  • Should I change what’s inside? 📝
  • Should I send a fake reply instead? 🎭
// You're now the traffic controller!
await page.route('**/*', route => {
  console.log('Caught a request!');
  route.continue(); // Let it pass
});

Why is this AMAZING?

  • Test your app without real servers
  • Check how your app handles errors
  • Speed up tests (no real network!)
  • Control exactly what data your app sees

🔧 The page.route() Method

Your Personal Traffic Light

page.route() intercepts requests for ONE page only.

The Recipe:

await page.route(
  'URL pattern',  // Which cars to catch
  handler         // What to do with them
);

Example: Catch All Images

// Block all PNG images
await page.route('**/*.png', route => {
  console.log('Blocked:', route.request().url());
  route.abort(); // STOP! No images allowed
});

Example: Catch API Calls

// Intercept API requests
await page.route('**/api/users', route => {
  console.log('API call intercepted!');
  route.continue();
});

URL Pattern Magic ✨

Pattern Catches
**/* Everything!
**/api/* Any URL with /api/
**/*.css All CSS files
https://example.com/** Only this domain
graph TD A["Request Made"] --> B{page.route Check} B -->|Matches Pattern| C["Your Handler Runs"] B -->|No Match| D["Request Goes Normally"] C --> E["abort / continue / fulfill"]

🌐 The browserContext.route() Method

The SUPER Traffic Controller

What if you have multiple pages open? browserContext.route() controls traffic for ALL of them!

const context = await browser.newContext();

// This affects EVERY page in this context
await context.route('**/api/**', route => {
  console.log('Caught from ANY page!');
  route.continue();
});

const page1 = await context.newPage();
const page2 = await context.newPage();
// Both pages share the same traffic rules!

When to Use Which?

Situation Use This
Testing one page page.route()
Multiple pages, same rules context.route()
Login across tabs context.route()
Page-specific mocking page.route()

Pro Tip: page.route() can OVERRIDE context.route() for specific pages!


🎭 Mocking API Responses

The Fake Delivery Truck

Sometimes you don’t want real data. You want to control EXACTLY what your app receives.

route.fulfill() = Send a fake response!

await page.route('**/api/users', route => {
  // Send fake data!
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify([
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' }
    ])
  });
});

Why Mock Responses?

  1. No server needed - Test without backend running
  2. Consistent data - Same test results every time
  3. Edge cases - Test errors, empty states, huge lists
  4. Speed - Instant responses, fast tests!

Mock Different Scenarios

// Mock an error response
await page.route('**/api/users', route => {
  route.fulfill({
    status: 500,
    body: JSON.stringify({ error: 'Server down!' })
  });
});
// Mock empty data
await page.route('**/api/products', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify([]) // Empty array
  });
});
graph TD A["App Requests /api/users"] --> B["Route Handler"] B --> C["route.fulfill"] C --> D["Fake Data Sent"] D --> E["App Shows Fake Users"]

✏️ Modifying Responses

The Editor in the Middle

What if you want the REAL response, but with some changes?

Story Time: Imagine the mail arrives, you open it, change one word, seal it back, and deliver it. That’s modifying responses!

await page.route('**/api/users', async route => {
  // Get the REAL response first
  const response = await route.fetch();
  const json = await response.json();

  // Modify it!
  json.forEach(user => {
    user.name = user.name.toUpperCase();
  });

  // Send modified version
  route.fulfill({
    response,
    body: JSON.stringify(json)
  });
});

Common Modifications

Add extra data:

await page.route('**/api/profile', async route => {
  const response = await route.fetch();
  const data = await response.json();

  // Add test badge
  data.badges = ['Tester', 'Premium'];

  route.fulfill({
    response,
    body: JSON.stringify(data)
  });
});

Change headers:

await page.route('**/*', async route => {
  const response = await route.fetch();

  route.fulfill({
    response,
    headers: {
      ...response.headers(),
      'X-Test-Header': 'Modified!'
    }
  });
});

🛑 Blocking Requests

The Bouncer at the Door

Sometimes you want to COMPLETELY stop certain requests.

route.abort() = DENIED! Go away!

// Block all tracking scripts
await page.route('**/*analytics*', route => {
  route.abort();
});

Common Blocking Patterns

Block images (faster tests):

await page.route('**/*.{png,jpg,jpeg,gif}', route => {
  route.abort();
});

Block third-party resources:

await page.route(route => {
  const url = route.request().url();
  // Block anything NOT from our domain
  if (!url.includes('myapp.com')) {
    return route.abort();
  }
  route.continue();
});

Block specific file types:

await page.route('**/*', route => {
  const url = route.request().url();
  if (url.endsWith('.css') || url.endsWith('.woff')) {
    route.abort();
  } else {
    route.continue();
  }
});

The Three Commands

Command What It Does
route.continue() Let request go normally
route.fulfill() Send your own response
route.abort() Block completely
graph TD A["Request Intercepted"] --> B{Your Decision} B -->|continue| C["Request Goes to Server"] B -->|fulfill| D["Your Fake Response"] B -->|abort| E["Request Blocked"] C --> F["Real Response"] D --> G["App Gets Your Data"] E --> H["Request Failed"]

🎮 Putting It All Together

Complete Example

const { chromium } = require('playwright');

async function demo() {
  const browser = await chromium.launch();
  const context = await browser.newContext();

  // Context-level: Block analytics everywhere
  await context.route('**/*analytics*', route => {
    route.abort();
  });

  const page = await context.newPage();

  // Page-level: Mock API
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'Test User' }
      ])
    });
  });

  // Page-level: Modify responses
  await page.route('**/api/config', async route => {
    const response = await route.fetch();
    const data = await response.json();
    data.theme = 'dark'; // Force dark mode
    route.fulfill({ response, body: JSON.stringify(data) });
  });

  await page.goto('https://myapp.com');
  // Your app now sees mocked/modified data!

  await browser.close();
}

🌟 Key Takeaways

  1. page.route() - Traffic controller for one page
  2. context.route() - Traffic controller for all pages
  3. route.continue() - Let it through
  4. route.fulfill() - Send fake response
  5. route.abort() - Block it completely
  6. route.fetch() - Get real response, then modify

You’re now the MASTER of network traffic! 🚀

Go ahead—mock that API, block those ads, modify those responses. Your tests are now faster, more reliable, and under YOUR control!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.