First-class Pydantic v2+ support
Summary
Add first-class support for Pydantic v2+ models in Strawberry GraphQL.
This PR introduces a new strawberry.pydantic module that allows you to directly decorate Pydantic BaseModel classes to create GraphQL types, inputs, and interfaces without requiring separate wrapper classes.
Basic Usage
import strawberry
from pydantic import BaseModel
@strawberry.pydantic.type
class User(BaseModel):
name: str
age: int
@strawberry.pydantic.input
class CreateUserInput(BaseModel):
name: str
age: int
@strawberry.pydantic.interface
class Node(BaseModel):
id: str
Features Implemented
Core Features
- ✅
@strawberry.pydantic.type- Convert Pydantic models to GraphQL types - ✅
@strawberry.pydantic.input- Convert Pydantic models to GraphQL input types - ✅
@strawberry.pydantic.interface- Convert Pydantic models to GraphQL interfaces - ✅ Automatic field extraction from Pydantic models
- ✅ Pydantic field descriptions preserved in GraphQL schema
- ✅ Pydantic field aliases used as GraphQL field names
- ✅ Support for
strawberry.Privateto exclude fields from schema - ✅ Support for
strawberry.field()withAnnotatedfor directives, permissions, deprecation - ✅ Generic Pydantic model support
- ✅ Nested Pydantic types
- ✅
strawberry.pydantic.Errortype for validation error handling - ✅ Computed fields via
include_computed=True
Pydantic v2 Features (130 Tests Passing)
- ✅ Functional Validators -
BeforeValidator,AfterValidator,WrapValidatorwithAnnotatedtypes - ✅ @model_validator Support - Cross-field validation with
mode='before'|'after'|'wrap' - ✅ Validation Context - Strawberry
Infoautomatically passed to Pydantic validators - ✅ model_config Settings -
strict=True,extra='forbid',from_attributes=Trueall respected - ✅ Strict Mode Per-Field -
Field(strict=True)for individual field validation - ✅ Separate Aliases -
validation_aliasandserialization_aliassupported viaby_name=True - ✅ Discriminated Unions -
Literaltype support for type discriminators - ✅ TypeAdapter - Use
TypeAdapterin resolvers for scalar/list validation - ✅ RootModel - Use
RootModelfor validated list/dict wrappers in resolvers
Migration from Experimental
# Before (experimental)
@strawberry.experimental.pydantic.type(model=UserModel, all_fields=True)
class User:
pass
# After (first-class)
@strawberry.pydantic.type
class User(BaseModel):
name: str
age: int
Validation Context Example
Pydantic validators can access GraphQL context for permission-based validation:
from pydantic import field_validator, ValidationInfo
@strawberry.pydantic.input
class CreatePostInput(BaseModel):
title: str
@field_validator('title')
@classmethod
def check_permissions(cls, v: str, info: ValidationInfo) -> str:
strawberry_info = info.context.get('info') if info.context else None
if strawberry_info:
user = strawberry_info.context.get('user')
if user and not user.can_create_posts:
raise ValueError('User cannot create posts')
return v
Discriminated Union Example
from typing import Literal
@strawberry.pydantic.type
class Cat(BaseModel):
pet_type: Literal["cat"]
meow_volume: int
@strawberry.pydantic.type
class Dog(BaseModel):
pet_type: Literal["dog"]
bark_volume: int
@strawberry.type
class Query:
@strawberry.field
def pet(self) -> Cat | Dog:
return Cat(pet_type="cat", meow_volume=10)
Test Results
- 130 Pydantic-specific tests - All passing ✅
- Full test suite (4675+ tests) - All passing ✅
🤖 Generated with Claude Code
Reviewer's Guide
This PR introduces first-class Pydantic support by adding a dedicated strawberry/pydantic module with type/input/interface decorators, overhauls the Pydantic integration documentation and migration guide, cleans up experimental code and unused flags, and restructures and extends the test suite to cover all new features.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Add first-class Pydantic integration decorators |
|
strawberry/pydantic/object_type.pystrawberry/pydantic/fields.pystrawberry/pydantic/__init__.pystrawberry/__init__.py |
| Overhaul Pydantic documentation and remove experimental section |
|
docs/integrations/pydantic.md |
| Restructure and extend test suite for Pydantic integration |
|
tests/pydantic/test_basic.pytests/pydantic/test_execution.pytests/pydantic/test_special_features.pytests/pydantic/test_queries_mutations.pytests/pydantic/test_nested_types.py |
| Cleanup unused experimental code and flags |
|
strawberry/experimental/pydantic/_compat.py |
| Update CLAUDE.md and PLAN.md with new integration status |
|
PLAN.mdCLAUDE.md |
Possibly linked issues
- #1: The PR implements first-class Pydantic integration, including new decorators and updated documentation, directly addressing Pydantic V2 support and migration.
Tips and commands
Interacting with Sourcery
- Trigger a new review: Comment
@sourcery-ai reviewon the pull request. - Continue discussions: Reply directly to Sourcery's review comments.
- Generate a GitHub issue from a review comment: Ask Sourcery to create an
issue from a review comment by replying to it. You can also reply to a
review comment with
@sourcery-ai issueto create an issue from it. - Generate a pull request title: Write
@sourcery-aianywhere in the pull request title to generate a title at any time. You can also comment@sourcery-ai titleon the pull request to (re-)generate the title at any time. - Generate a pull request summary: Write
@sourcery-ai summaryanywhere in the pull request body to generate a PR summary at any time exactly where you want it. You can also comment@sourcery-ai summaryon the pull request to (re-)generate the summary at any time. - Generate reviewer's guide: Comment
@sourcery-ai guideon the pull request to (re-)generate the reviewer's guide at any time. - Resolve all Sourcery comments: Comment
@sourcery-ai resolveon the pull request to resolve all Sourcery comments. Useful if you've already addressed all the comments and don't want to see them anymore. - Dismiss all Sourcery reviews: Comment
@sourcery-ai dismisson the pull request to dismiss all existing Sourcery reviews. Especially useful if you want to start fresh with a new review - don't forget to comment@sourcery-ai reviewto trigger a new review!
Customizing Your Experience
Access your dashboard to:
- Enable or disable review features such as the Sourcery-generated pull request summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.
Getting Help
- Contact our support team for questions or feedback.
- Visit our documentation for detailed guides and information.
- Keep in touch with the Sourcery team by following us on X/Twitter, LinkedIn or GitHub.
Codecov Report
:x: Patch coverage is 56.78818% with 1375 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 91.27%. Comparing base (c03892c) to head (7067816).
Additional details and impacted files
@@ Coverage Diff @@
## main #3965 +/- ##
==========================================
- Coverage 94.41% 91.27% -3.14%
==========================================
Files 536 563 +27
Lines 35036 38186 +3150
Branches 1842 1913 +71
==========================================
+ Hits 33079 34856 +1777
- Misses 1659 3023 +1364
- Partials 298 307 +9
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
CodSpeed Performance Report
Merging #3965 will not alter performance
Comparing feature/pydantic-first-class (7067816) with main (c03892c)
Summary
✅ 28 untouched
/pre-release
Pre-release
:wave:
Pre-release 0.279.0.dev.1754159379 [70130b4618633d5086793cc6e0b903c56ff1dc14] has been released on PyPi! :rocket: You can try it by doing:
poetry add strawberry-graphql==0.279.0.dev.1754159379
/pre-release
/pre-release