APQ Debugger

June 12, 2026 · View on GitHub

APQ Debugger — Chrome extension for Apollo GraphQL Persisted Queries (APQ)

APQ Debugger reveals the full GraphQL operation behind Apollo Client's Automatic Persisted Queries (APQ) hashes and visualizes the fallback flow in Chrome DevTools. → Install from Chrome Web Store

Features

  • Network Interception: Intercept and modify GraphQL requests in real-time
  • Pattern Matching: Use URL patterns to target specific GraphQL endpoints
  • DevTools Integration: Seamless integration with Chrome DevTools
  • APQ Fallback Visualization: See both the hash-only and full-query phases of APQ requests
  • Request History: Browse, filter, and inspect intercepted requests with syntax highlighting
  • Passive Mode: Resolve APQ hashes from a persisted hash registry without modifying any traffic
  • Schema Explorer: Auto-detect the GraphQL endpoint and browse its introspected schema
  • Copy as cURL: Replay any captured request from Bash or PowerShell, including captured request headers

Installation

  1. Clone this repository
  2. Run npm install and npm run build to produce the extension_build/ folder
  3. Open Chrome and go to chrome://extensions/
  4. Enable "Developer mode"
  5. Click "Load unpacked" and select the extension_build/ folder
  6. Open DevTools and look for the "APQ Debugger" panel

Usage

  1. Navigate to the target page with Apollo Client
  2. Open Chrome DevTools and go to the "APQ Debugger" panel
  3. Add URL patterns to match GraphQL endpoints (e.g., *graphql*, */api/*)
  4. Click "Start Debugging" to begin interception
  5. Intercepted requests will trigger twice:
    • First: Failing due to PersistedQueryNotFound
    • Second: Containing the full GraphQL query

File Structure

apq-debugger/
├── frontend/
│   ├── devtools.html              # Main DevTools panel HTML
│   └── devtools.css               # Panel styles (dark/light theme)
├── js/
│   ├── shared/                    # Constants & logger shared by both bundles
│   ├── sw/                        # Service worker modules
│   │   ├── index.js               # Entry point – event wiring
│   │   ├── chrome-api.js          # Promise wrappers for Chrome APIs
│   │   ├── debugger-manager.js    # Attach/detach/restore lifecycle
│   │   ├── interceptor.js         # Fetch.requestPaused handler
│   │   ├── messaging.js           # onMessage router & action toggle
│   │   ├── storage.js             # chrome.storage.local helpers
│   │   ├── hash.js                # SHA-256 digest utility
│   │   ├── hash-registry.js       # Persisted hash → query registry (passive mode)
│   │   ├── endpoint-tracker.js    # GraphQL endpoints observed per tab
│   │   └── schema-loader.js       # Endpoint auto-detection & introspection
│   ├── ui/                        # DevTools panel modules
│   │   ├── index.js               # Entry point – panel creation
│   │   ├── state.js               # Shared reactive state
│   │   ├── dom-helpers.js         # DOM query & escape utilities
│   │   ├── patterns.js            # URL pattern CRUD & storage sync
│   │   ├── request-list.js        # Request list rendering & filtering
│   │   ├── request-detail.js      # Detail panel & copy-to-clipboard
│   │   ├── curl-copy.js           # Copy-as-cURL generators & modal
│   │   ├── status.js              # Status badge & banner management
│   │   ├── debugger-controls.js   # Start/stop button orchestration
│   │   ├── registry-controls.js   # Hash registry & passive mode controls
│   │   ├── schema-controls.js     # Schema loading controls & progress
│   │   ├── schema-viewer.js       # Introspected schema explorer
│   │   ├── settings.js            # Panel settings
│   │   ├── toolbar.js             # Toolbar actions
│   │   ├── keyboard.js            # Keyboard navigation
│   │   ├── layout.js              # Panel layout management
│   │   └── resize.js              # Resizable panel dividers
├── scripts/
│   ├── sync-version.js            # Sync package.json → manifest.json version
│   └── package-extension.js       # Zip extension_build/ for Web Store upload
├── tests/
│   ├── unit/                      # Jest unit tests
│   └── e2e/                       # Playwright end-to-end tests
├── icons/                         # Extension icons (16/32/48/128 px)
├── store/                         # Chrome Web Store assets
├── .github/workflows/             # CI/CD (lint, test, build, release)
├── manifest.json                  # Chrome extension manifest (MV3)
├── package.json                   # Node.js project metadata
├── build-enhanced.js              # esbuild-powered build script
└── README.md                      # This file

Development

Quick Start

  1. Install dependencies:

    npm install
    
  2. Build the extension:

    npm run build
    
  3. Watch mode for development:

    npm run watch
    
  4. Load the extension:

    • Go to chrome://extensions/ and enable Developer mode
    • Click "Load unpacked" and select the extension_build/ folder

Available Scripts

ScriptDescription
npm run buildBundle & minify JS/CSS into extension_build/
npm run build:devDevelopment build with source maps
npm run watchWatch mode — rebuild on file changes
npm testRun unit tests (Jest)
npm run test:e2eRun end-to-end tests (Playwright)
npm run lintLint JS with ESLint
npm run lint:fixAuto-fix lint issues
npm run formatFormat code with Prettier
npm run packageZip extension_build/ for Chrome Web Store
npm run releaseBuild + package in one step

Releasing a New Version

# Bump version (auto-syncs manifest.json and creates a git tag)
npm version patch   # or minor / major

# Push the tag to trigger the release workflow
git push --follow-tags

The GitHub Actions release workflow will automatically build, test, and create a GitHub Release with the .zip artifact.

Technical Details

How It Works

  1. Pattern Matching: Uses the Chrome Debugger API's Fetch.requestPaused event to intercept requests matching user-specified URL patterns
  2. Hash Contamination: Replaces the SHA-256 hash in APQ-only requests with a bogus value, forcing Apollo Client to retry with the full query
  3. Query Capture: Captures the full GraphQL query from the retry request and sends it to the DevTools panel
  4. Real-time Display: The DevTools panel shows intercepted requests with filtering, syntax highlighting, and one-click copy

Architecture

  • Service Worker (js/sw/): Background process handling network interception via the Chrome Debugger API
  • DevTools Panel (js/ui/): User interface for configuration and live request monitoring
  • Message Passing: Bidirectional communication between the panel and service worker via chrome.runtime.sendMessage
  • Persistent State: URL patterns and attached-tab state are persisted in chrome.storage.local to survive service worker restarts

Privacy Note

The Copy as cURL feature includes the captured request headers — which may contain cookies and authorization tokens — in the generated command so the request can be replayed as-is. This data never leaves your browser; it is only written to your clipboard when you click Copy.

Contributing

See CONTRIBUTING.md for development setup, architecture details, and contribution guidelines.

License

This project is licensed under the Apache License 2.0 — see the LICENSE file for details.