GraphQL Debugger (formerly APQ Debugger) works on network traffic, so it works with any GraphQL client on any site. It 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
- Network Interception: Intercept and modify GraphQL requests in real-time
- Request Breakpoints: Pause a request by operation name (or the next one), edit its query, variables and headers, then resume or fail it
- 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
- Clone this repository
- Run
npm installandnpm run buildto produce theextension_build/folder - Open Chrome and go to
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked" and select the
extension_build/folder - Open DevTools and look for the "GraphQL Debugger" panel
- Navigate to the target page with Apollo Client
- Open Chrome DevTools and go to the "GraphQL Debugger" panel
- Add URL patterns to match GraphQL endpoints (e.g.,
*graphql*,*/api/*) - Click "Start Debugging" to begin interception
- Intercepted requests will trigger twice:
- First: Failing due to
PersistedQueryNotFound - Second: Containing the full GraphQL query
- First: Failing due to
Breakpoints hold a matching request in the service worker before it is sent; only the request is held, the page keeps running.
- Start interception on the Requests tab, then open the Breakpoints tab
- Add an operation name (e.g.
GetCart) and turn on Debug mode, or press Pause on next request to catch whatever is sent next - When a request pauses, edit its Query, Variables or Headers, then Resume this (or Resume all, F8), or Fail request to make the page's fetch fail
- Invalid JSON in Variables or Headers blocks the resume with an inline error. An edited query gets its persisted-query hash recomputed
In active mode a hash-only request is never paused (it is still swapped as usual); its full-query retry is. In passive mode hash-only requests pause too. Requests are only held while the panel is open: closing it, navigating, closing the tab or stopping interception sends every held request on unmodified.
apq-debugger/
├── frontend/
│ ├── devtools.html # Main DevTools panel HTML
│ ├── css/ # Kora design system layer
│ │ ├── tokens.css # Color/type/spacing tokens (light-dark() theming)
│ │ ├── theme-dark.css # Dark-mode gates (explicit + system preference)
│ │ ├── density.css # Comfortable/Compact density scales
│ │ └── components.css # Panel components in the Kora idiom
│ └── fonts/ # Bundled woff2 (Italiana, Albert Sans,
│ # Spline Sans Mono — all SIL OFL 1.1)
├── 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
│ │ ├── breakpoints.js # Breakpoint list, debug mode, pause decision
│ │ ├── pause-queue.js # Held requests: hold, resume, fail, release
│ │ ├── resume-body.js # Build the resumed request from panel edits
│ │ ├── panel-presence.js # Which tabs have a panel connected (ports)
│ │ ├── 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
│ │ ├── tabs.js # Requests / Schema / Breakpoints tab switching
│ │ ├── panel-port.js # Port telling the SW this panel is open
│ │ ├── breakpoints-view.js # Breakpoints tab: toolbar, paused queue, list
│ │ ├── paused-editor.js # Edit, resume or fail a paused request
│ │ ├── 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
-
Install dependencies:
npm install
-
Build the extension:
npm run build
-
Watch mode for development:
npm run watch
-
Load the extension:
- Go to
chrome://extensions/and enable Developer mode - Click "Load unpacked" and select the
extension_build/folder
- Go to
| Script | Description |
|---|---|
npm run build |
Bundle & minify JS/CSS into extension_build/ |
npm run build:dev |
Development build with source maps |
npm run watch |
Watch mode — rebuild on file changes |
npm test |
Run unit tests (Jest) |
npm run test:e2e |
Run end-to-end tests (Playwright) |
npm run lint |
Lint JS with ESLint |
npm run lint:fix |
Auto-fix lint issues |
npm run format |
Format code with Prettier |
npm run package |
Zip extension_build/ for Chrome Web Store |
npm run release |
Build + package in one step |
# 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-tagsThe GitHub Actions release workflow will automatically build, test, and create a GitHub Release with the .zip artifact.
- Pattern Matching: Uses the Chrome Debugger API's
Fetch.requestPausedevent to intercept requests matching user-specified URL patterns - Hash Contamination: Replaces the SHA-256 hash in APQ-only requests with a bogus value, forcing Apollo Client to retry with the full query
- Query Capture: Captures the full GraphQL query from the retry request and sends it to the DevTools panel
- Real-time Display: The DevTools panel shows intercepted requests with filtering, syntax highlighting, and one-click copy
- 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, plus a long-lived port per open panel so requests are only held while a panel is connected - Persistent State: URL patterns and attached-tab state are persisted in
chrome.storage.localto survive service worker restarts
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.
Your GraphQL traffic (URLs, queries, variables, headers, hashes, schemas) never leaves your browser. Official builds can optionally send anonymous feature-usage counts (which features are used, extension and browser version, OS, UI language) to Google Analytics, but only if you opt in; nothing is sent until you do, and you can switch it off any time in the panel settings, which also deletes the random IDs. Builds from source have no analytics configured. See the privacy policy.
See CONTRIBUTING.md for development setup, architecture details, and contribution guidelines.
This project is licensed under the Apache License 2.0 — see the LICENSE file for details.