feat: add GraphQL Panel with Strawberry integration#15
Conversation
Implements Phase 10 of the debug toolbar development plan: GraphQL & API Debugging. Features: - TrackedOperation and TrackedResolver dataclasses for GraphQL tracking - DebugToolbarExtension (Strawberry SchemaExtension) for operation/resolver instrumentation - GraphQLPanel with stats generation and resolver tree building - N+1 resolver pattern detection with DataLoader suggestions - Duplicate operation detection using MD5 hashing - Stack trace capture for resolver debugging - Server-Timing header integration - Configurable thresholds for slow operations/resolvers The extension integrates with Strawberry GraphQL >= 0.240.0 and hooks into: - on_operation: Tracks query/mutation/subscription lifecycle - resolve: Measures individual field resolver timing Added comprehensive unit tests achieving 90%+ coverage for all components. Added graphql_panel_example.py demonstrating Strawberry + Litestar integration. Updated example frontends to document the new panel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
There was a problem hiding this comment.
Pull request overview
This PR implements Phase 10 of the debug toolbar development plan by adding comprehensive GraphQL operation and resolver tracking for Strawberry GraphQL applications. The implementation provides N+1 query detection, duplicate operation detection, timing analysis, and stack trace capture to help developers identify and fix GraphQL performance issues.
Key Features:
- Operation and resolver tracking with hierarchical visualization
- N+1 query pattern detection with DataLoader suggestions (configurable threshold, default: 3 occurrences)
- Duplicate operation detection using MD5 hashing
- Slow operation/resolver highlighting (configurable thresholds: 100ms operations, 10ms resolvers)
- Stack trace capture for debugging resolver execution paths
- Server-Timing header integration for performance monitoring
Integration:
The panel integrates via a Strawberry SchemaExtension that hooks into on_operation and resolve lifecycle methods, storing tracked data in the debug toolbar's RequestContext. Requires strawberry-graphql >= 0.240.0.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/debug_toolbar/extras/strawberry/__init__.py |
Module initialization with public API exports |
src/debug_toolbar/extras/strawberry/models.py |
TrackedOperation and TrackedResolver dataclasses for storing GraphQL execution metadata |
src/debug_toolbar/extras/strawberry/extension.py |
DebugToolbarExtension SchemaExtension for instrumenting Strawberry operations and resolvers |
src/debug_toolbar/extras/strawberry/panel.py |
GraphQLPanel for displaying operations, generating stats, and building resolver trees |
src/debug_toolbar/extras/strawberry/analyzers.py |
N1Analyzer and DuplicateDetector for identifying performance patterns |
src/debug_toolbar/extras/strawberry/utils.py |
Utility functions for stack capture, query truncation, and variable formatting |
tests/unit/extras/strawberry/ |
Comprehensive test suite with 65 new tests covering all components |
examples/graphql_panel_example.py |
Example application demonstrating GraphQL Panel usage with N+1 patterns |
pyproject.toml |
Added strawberry optional dependency group |
uv.lock |
Locked strawberry-graphql@0.287.0 with dependencies |
examples/litestar_basic/app.py |
Documentation update listing GraphQL Panel |
examples/litestar_advanced_alchemy/app.py |
Documentation update listing GraphQL Panel |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds HTML rendering for the GraphQL Panel to display tracked operations, resolvers, and analysis results: - Summary stats: operations, total time, resolvers, N+1 issues, duplicates - N+1 pattern warnings with field names, counts, durations, suggestions - Operation list with query preview, resolver counts, slow resolver highlights - CSS styling with dark/light theme support matching existing panels - Refactored _render_panel_content to use dispatch dict pattern (PLR0911 fix) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The /users endpoint was accessing u.posts in a sync context (f-string) which triggered lazy loading. Added load=[User.posts] to the repository list call to eagerly load the relationship, matching the pattern already used in posts_page for Post.author. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Added GraphQL Panel demo section to the advanced_alchemy example index page with instructions for running the separate Strawberry GraphQL demo. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Adds `make example-graphql` to run the Strawberry GraphQL demo on port 8003. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
examples/litestar_advanced_alchemy/app.py:137
- [nitpick] The
load=[User.posts]parameter is added to the query, but this appears to be unrelated to the GraphQL panel feature. This change seems to be modifying the example to use eager loading for the SQLAlchemy relationship.
While this is likely a good practice for performance, it's not directly related to the GraphQL panel implementation and should be in a separate commit or have a comment explaining why it's part of this PR (e.g., "Eager load posts to avoid N+1 in non-GraphQL contexts").
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The Strawberry extension wasn't receiving the debug toolbar context because async context propagation can fail across Strawberry's internal execution. Changes: - Add _get_debug_context() helper to extension that tries contextvar first, then falls back to Strawberry's execution_context.context - Update graphql_panel_example to inject context via context_getter - Add GraphQLContext dataclass with debug_toolbar_context field Now the GraphQL Panel properly tracks operations, resolvers, and detects N+1 patterns when running queries through Strawberry. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ntext) Changed GraphQLContext from dataclass to dict to satisfy Strawberry's InvalidCustomContext validation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 25 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The contextvar wasn't propagating to Strawberry's extension execution. Now storing the context in scope["state"] so it can be retrieved via the Litestar Request in the context_getter. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…efix Using scope["_debug_toolbar_context"] instead of scope["state"]["..."] to avoid potential issues with nested dict access. Added debug logging to trace context propagation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
GraphQL queries return JSON, so the toolbar isn't visible in responses. Added clear instructions on the index page explaining users need to: 1. Run a query in the playground 2. Check /_debug_toolbar/ request history 3. Click the POST /graphql request to see panel data 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 26 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add module-level logger instead of creating in method - Call super().__init__() when Strawberry is available - Fix resolver tree building for deeply nested paths by walking up to find closest existing ancestor 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add inline script dependencies to all example files using PEP 723 format. Each example now declares its own dependencies, allowing direct execution with `uv run examples/xxx.py`. Also adds `if __name__ == "__main__"` blocks to web app examples for direct execution with uvicorn. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Update example Makefile targets to use simpler `uv run examples/xxx.py` instead of `uv run --extra xxx litestar --app ...` commands now that examples have PEP 723 script metadata. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Wrap on_operation yield in try-finally for proper cleanup on errors - Handle async resolvers correctly using inspect.isawaitable - Use full MD5 hash instead of truncated for duplicate detection - Fix duplicate operations display to show unique/total counts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 35 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Implements Phase 10: GraphQL & API Debugging of the debug toolbar development plan.
Features
Integration
The extension integrates with Strawberry GraphQL >= 0.240.0 and hooks into:
on_operation: Tracks query/mutation/subscription lifecycleresolve: Measures individual field resolver timingUsage
Install with:
pip install debug-toolbar[strawberry]Files Changed
src/debug_toolbar/extras/strawberry/- New Strawberry integration modulemodels.py- TrackedOperation, TrackedResolver dataclassesextension.py- DebugToolbarExtension (Strawberry SchemaExtension)panel.py- GraphQLPanel with stats generationanalyzers.py- N1Analyzer, DuplicateDetectorutils.py- StackCapture, formatting utilitiesexamples/graphql_panel_example.py- New example demonstrating usagepyproject.toml- Addedstrawberryoptional dependencyTest plan
make lint- passesmake type-check- passesmake test- 479 tests pass (65 new tests for GraphQL components)🤖 Generated with Claude Code