Skip to content

feat: add GraphQL Panel with Strawberry integration#15

Merged
JacobCoffee merged 14 commits intomainfrom
feat/graphql-panel
Nov 30, 2025
Merged

feat: add GraphQL Panel with Strawberry integration#15
JacobCoffee merged 14 commits intomainfrom
feat/graphql-panel

Conversation

@JacobCoffee
Copy link
Owner

Summary

Implements Phase 10: GraphQL & API Debugging of the debug toolbar development plan.

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 (default: 100ms operations, 10ms resolvers)

Integration

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

Usage

import strawberry
from debug_toolbar.extras.strawberry import DebugToolbarExtension, GraphQLPanel
from debug_toolbar.litestar import DebugToolbarPlugin, LitestarDebugToolbarConfig

schema = strawberry.Schema(
    query=Query,
    extensions=[DebugToolbarExtension()],
)

toolbar_config = LitestarDebugToolbarConfig(
    extra_panels=[GraphQLPanel],
)

Install with: pip install debug-toolbar[strawberry]

Files Changed

  • src/debug_toolbar/extras/strawberry/ - New Strawberry integration module
    • models.py - TrackedOperation, TrackedResolver dataclasses
    • extension.py - DebugToolbarExtension (Strawberry SchemaExtension)
    • panel.py - GraphQLPanel with stats generation
    • analyzers.py - N1Analyzer, DuplicateDetector
    • utils.py - StackCapture, formatting utilities
  • examples/graphql_panel_example.py - New example demonstrating usage
  • pyproject.toml - Added strawberry optional dependency
  • Updated example frontends to document the new panel

Test plan

  • Run make lint - passes
  • Run make type-check - passes
  • Run make test - 479 tests pass (65 new tests for GraphQL components)
  • Verify N+1 detection works with test case (5 repeated resolvers triggers alert)
  • Verify duplicate detection works (same query+variables detected)
  • Manual test with graphql_panel_example.py

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings November 30, 2025 05:56
@github-actions
Copy link
Contributor

github-actions bot commented Nov 30, 2025

PR Preview Action v1.6.3

🚀 View preview at
https://JacobCoffee.github.io/debug-toolbar/pr-preview/pr-15/

Built to branch gh-pages at 2025-11-30 07:11 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

JacobCoffee and others added 2 commits November 30, 2025 00:01
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>
Copilot AI review requested due to automatic review settings November 30, 2025 06:02
JacobCoffee and others added 2 commits November 30, 2025 00:03
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>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

JacobCoffee and others added 2 commits November 30, 2025 00:07
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>
Copilot AI review requested due to automatic review settings November 30, 2025 06:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

JacobCoffee and others added 2 commits November 30, 2025 00:11
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>
Copilot AI review requested due to automatic review settings November 30, 2025 06:13
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>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

JacobCoffee and others added 3 commits November 30, 2025 00:22
- 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>
Copilot AI review requested due to automatic review settings November 30, 2025 07:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@JacobCoffee JacobCoffee enabled auto-merge (squash) November 30, 2025 07:21
@JacobCoffee JacobCoffee merged commit af70e9c into main Nov 30, 2025
24 checks passed
@JacobCoffee JacobCoffee deleted the feat/graphql-panel branch November 30, 2025 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants