or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-types.mdexperimental.mdextensions.mdfederation.mdfields-resolvers.mdframework-integrations.mdindex.mdrelay.mdschema-execution.mdutilities.md

experimental.mddocs/

0

# Experimental Features

1

2

Experimental features including Pydantic integration and preview functionality that may change in future versions. These features are under active development and may have breaking changes in minor releases.

3

4

⚠️ **Warning**: Experimental features are not covered by semantic versioning and may change or be removed in future releases. Use with caution in production environments.

5

6

## Capabilities

7

8

### Pydantic Integration

9

10

Integration with Pydantic models for automatic GraphQL type generation and validation.

11

12

```python { .api }

13

# Pydantic type decorators

14

def pydantic.type(

15

model: Type[BaseModel],

16

*,

17

name: str = None,

18

description: str = None,

19

all_fields: bool = False

20

) -> Any:

21

"""

22

Convert Pydantic model to GraphQL object type.

23

24

Args:

25

model: Pydantic model class

26

name: Custom GraphQL type name

27

description: Type description

28

all_fields: Include all model fields (including private)

29

30

Returns:

31

GraphQL object type based on Pydantic model

32

"""

33

34

def pydantic.input(

35

model: Type[BaseModel],

36

*,

37

name: str = None,

38

description: str = None,

39

all_fields: bool = False

40

) -> Any:

41

"""

42

Convert Pydantic model to GraphQL input type.

43

44

Args:

45

model: Pydantic model class

46

name: Custom GraphQL input type name

47

description: Input type description

48

all_fields: Include all model fields

49

50

Returns:

51

GraphQL input type based on Pydantic model

52

"""

53

54

def pydantic.interface(

55

model: Type[BaseModel],

56

*,

57

name: str = None,

58

description: str = None

59

) -> Any:

60

"""

61

Convert Pydantic model to GraphQL interface type.

62

63

Args:

64

model: Pydantic model class

65

name: Custom GraphQL interface name

66

description: Interface description

67

68

Returns:

69

GraphQL interface type based on Pydantic model

70

"""

71

```

72

73

**Usage Examples:**

74

75

```python

76

from pydantic import BaseModel, Field, validator

77

import strawberry.experimental.pydantic

78

79

# Define Pydantic models

80

class UserModel(BaseModel):

81

id: int

82

name: str = Field(description="User's full name")

83

email: str = Field(description="User's email address")

84

age: int = Field(ge=0, le=150, description="User's age")

85

is_active: bool = True

86

87

@validator('email')

88

def validate_email(cls, v):

89

# Pydantic validation logic

90

if '@' not in v:

91

raise ValueError('Invalid email format')

92

return v.lower()

93

94

class CreateUserModel(BaseModel):

95

name: str = Field(min_length=1, max_length=100)

96

email: str

97

age: int = Field(ge=0, le=150)

98

99

# Convert to GraphQL types

100

@strawberry.experimental.pydantic.type(UserModel)

101

class User:

102

pass # Fields are automatically generated from Pydantic model

103

104

@strawberry.experimental.pydantic.input(CreateUserModel)

105

class CreateUserInput:

106

pass # Input fields generated from Pydantic model

107

108

# Use in GraphQL schema

109

@strawberry.type

110

class Query:

111

@strawberry.field

112

def user(self, id: int) -> User:

113

# Pydantic validation happens automatically

114

user_data = get_user_from_database(id)

115

return User.from_pydantic(UserModel(**user_data))

116

117

@strawberry.type

118

class Mutation:

119

@strawberry.mutation

120

def create_user(self, input: CreateUserInput) -> User:

121

# Input is automatically validated by Pydantic

122

user_model = input.to_pydantic() # Convert to Pydantic model

123

124

# Pydantic validation runs here

125

if not user_model.email:

126

raise ValueError("Email is required")

127

128

# Create user

129

user_data = create_user_in_database(user_model.dict())

130

return User.from_pydantic(UserModel(**user_data))

131

132

schema = strawberry.Schema(query=Query, mutation=Mutation)

133

```

134

135

### Advanced Pydantic Integration

136

137

```python

138

from typing import Optional, List

139

from pydantic import BaseModel, Field

140

from datetime import datetime

141

import strawberry.experimental.pydantic

142

143

# Complex Pydantic model with relationships

144

class AddressModel(BaseModel):

145

street: str

146

city: str

147

state: str

148

zip_code: str = Field(alias="zipCode")

149

150

class UserModel(BaseModel):

151

id: int

152

name: str

153

email: str

154

addresses: List[AddressModel] = []

155

created_at: datetime = Field(alias="createdAt")

156

metadata: dict = Field(default_factory=dict)

157

158

class Config:

159

# Pydantic configuration

160

allow_population_by_field_name = True

161

json_encoders = {

162

datetime: lambda v: v.isoformat()

163

}

164

165

# Convert complex model

166

@strawberry.experimental.pydantic.type(UserModel, all_fields=True)

167

class User:

168

# Additional GraphQL-specific fields

169

@strawberry.field

170

def full_address(self) -> Optional[str]:

171

if not self.addresses:

172

return None

173

addr = self.addresses[0]

174

return f"{addr.street}, {addr.city}, {addr.state} {addr.zip_code}"

175

176

# Partial model conversion (only specific fields)

177

@strawberry.experimental.pydantic.type(UserModel)

178

class PublicUser:

179

# Only expose safe fields

180

id: strawberry.auto

181

name: strawberry.auto

182

created_at: strawberry.auto

183

```

184

185

### Pydantic Error Handling

186

187

Error type conversion from Pydantic validation errors.

188

189

```python { .api }

190

def pydantic.error_type(

191

model: Type[BaseModel],

192

*,

193

name: str = None

194

) -> Any:

195

"""

196

Create GraphQL error type from Pydantic model validation errors.

197

198

Args:

199

model: Pydantic model class

200

name: Custom error type name

201

202

Returns:

203

GraphQL type for validation errors

204

"""

205

```

206

207

**Usage Example:**

208

209

```python

210

from pydantic import BaseModel, ValidationError

211

import strawberry.experimental.pydantic

212

213

class UserValidationModel(BaseModel):

214

name: str = Field(min_length=2, max_length=50)

215

email: str = Field(regex=r'^[^@]+@[^@]+\.[^@]+$')

216

age: int = Field(ge=13, le=120)

217

218

@strawberry.experimental.pydantic.error_type(UserValidationModel)

219

class UserValidationError:

220

pass

221

222

@strawberry.type

223

class CreateUserResult:

224

user: Optional[User]

225

errors: Optional[List[UserValidationError]]

226

227

@strawberry.type

228

class Mutation:

229

@strawberry.mutation

230

def create_user_with_validation(

231

self,

232

name: str,

233

email: str,

234

age: int

235

) -> CreateUserResult:

236

try:

237

# Validate with Pydantic

238

validated_data = UserValidationModel(

239

name=name,

240

email=email,

241

age=age

242

)

243

244

# Create user

245

user = create_user(validated_data.dict())

246

return CreateUserResult(user=user, errors=None)

247

248

except ValidationError as e:

249

# Convert Pydantic errors to GraphQL errors

250

errors = [

251

UserValidationError.from_pydantic_error(error)

252

for error in e.errors()

253

]

254

return CreateUserResult(user=None, errors=errors)

255

```

256

257

### Pydantic Exceptions

258

259

Exception handling for Pydantic integration.

260

261

```python { .api }

262

class UnregisteredTypeException(Exception):

263

"""Exception raised when trying to use unregistered Pydantic type."""

264

265

def __init__(self, type_name: str):

266

self.type_name = type_name

267

super().__init__(f"Pydantic type '{type_name}' is not registered")

268

```

269

270

**Usage Example:**

271

272

```python

273

try:

274

# This might raise UnregisteredTypeException

275

@strawberry.experimental.pydantic.type(SomeUnregisteredModel)

276

class SomeType:

277

pass

278

except strawberry.experimental.pydantic.UnregisteredTypeException as e:

279

print(f"Type registration failed: {e.type_name}")

280

# Handle the error appropriately

281

```

282

283

## Advanced Experimental Patterns

284

285

### Pydantic Model Inheritance

286

287

```python

288

from pydantic import BaseModel

289

import strawberry.experimental.pydantic

290

291

# Base Pydantic model

292

class BaseEntity(BaseModel):

293

id: int

294

created_at: datetime

295

updated_at: datetime

296

297

# Derived models

298

class UserModel(BaseEntity):

299

name: str

300

email: str

301

302

class PostModel(BaseEntity):

303

title: str

304

content: str

305

author_id: int

306

307

# Convert to GraphQL with inheritance

308

@strawberry.experimental.pydantic.interface(BaseEntity)

309

class Entity:

310

pass

311

312

@strawberry.experimental.pydantic.type(UserModel)

313

class User(Entity):

314

pass

315

316

@strawberry.experimental.pydantic.type(PostModel)

317

class Post(Entity):

318

pass

319

```

320

321

### Custom Field Mapping

322

323

```python

324

from pydantic import BaseModel, Field

325

import strawberry.experimental.pydantic

326

327

class UserModel(BaseModel):

328

user_id: int = Field(alias="id")

329

full_name: str = Field(alias="name")

330

email_address: str = Field(alias="email")

331

332

# Custom field mapping

333

@strawberry.experimental.pydantic.type(

334

UserModel,

335

name="User",

336

description="User account with custom field mapping"

337

)

338

class User:

339

# Override specific fields

340

@strawberry.field(name="displayName")

341

def full_name(self) -> str:

342

return self.full_name.title()

343

344

# Add computed fields

345

@strawberry.field

346

def username(self) -> Optional[str]:

347

return self.email_address.split('@')[0] if self.email_address else None

348

```

349

350

### Pydantic with DataLoader

351

352

```python

353

from pydantic import BaseModel

354

from strawberry.dataloader import DataLoader

355

import strawberry.experimental.pydantic

356

357

class UserModel(BaseModel):

358

id: int

359

name: str

360

email: str

361

department_id: int

362

363

class DepartmentModel(BaseModel):

364

id: int

365

name: str

366

description: str

367

368

@strawberry.experimental.pydantic.type(UserModel)

369

class User:

370

@strawberry.field

371

async def department(self, info: strawberry.Info) -> "Department":

372

# Use DataLoader with Pydantic models

373

dept_data = await info.context.department_loader.load(self.department_id)

374

return Department.from_pydantic(DepartmentModel(**dept_data))

375

376

@strawberry.experimental.pydantic.type(DepartmentModel)

377

class Department:

378

pass

379

380

# DataLoader for departments

381

async def load_departments(dept_ids: List[int]) -> List[DepartmentModel]:

382

departments_data = await database.fetch_departments_by_ids(dept_ids)

383

return [DepartmentModel(**dept) for dept in departments_data]

384

385

department_loader = DataLoader(load_departments)

386

```

387

388

### Pydantic Subscriptions

389

390

```python

391

from pydantic import BaseModel

392

import strawberry.experimental.pydantic

393

from typing import AsyncIterator

394

395

class NotificationModel(BaseModel):

396

id: int

397

user_id: int

398

message: str

399

type: str

400

created_at: datetime

401

402

@strawberry.experimental.pydantic.type(NotificationModel)

403

class Notification:

404

pass

405

406

@strawberry.type

407

class Subscription:

408

@strawberry.subscription

409

async def user_notifications(

410

self,

411

user_id: int

412

) -> AsyncIterator[Notification]:

413

# Subscribe to user notifications

414

async for notification_data in notification_stream(user_id):

415

# Pydantic validation on streaming data

416

validated_notification = NotificationModel(**notification_data)

417

yield Notification.from_pydantic(validated_notification)

418

```

419

420

## Experimental Configuration

421

422

### Feature Flags

423

424

```python

425

import strawberry

426

from strawberry.experimental import enable_pydantic_integration

427

428

# Enable experimental features

429

enable_pydantic_integration()

430

431

# Or configure specific experimental features

432

strawberry.experimental.configure(

433

pydantic_integration=True,

434

auto_camel_case_pydantic=True,

435

strict_pydantic_validation=True

436

)

437

```

438

439

### Migration Path

440

441

```python

442

# Gradual migration from regular Strawberry to Pydantic integration

443

@strawberry.type

444

class User:

445

id: strawberry.ID

446

name: str

447

email: str

448

449

# Gradually add Pydantic validation

450

@strawberry.field

451

def validated_profile(self) -> "UserProfile":

452

# Use Pydantic for new fields

453

profile_data = get_user_profile(self.id)

454

return UserProfile.from_pydantic(UserProfileModel(**profile_data))

455

456

# New features use Pydantic from the start

457

class UserProfileModel(BaseModel):

458

bio: str = Field(max_length=500)

459

website: Optional[str] = Field(regex=r'^https?://')

460

social_links: List[str] = []

461

462

@strawberry.experimental.pydantic.type(UserProfileModel)

463

class UserProfile:

464

pass

465

```

466

467

## Limitations and Considerations

468

469

### Current Limitations

470

471

1. **Stability**: Experimental features may have breaking changes

472

2. **Performance**: Some features may have performance implications

473

3. **Documentation**: Limited documentation compared to stable features

474

4. **Third-party Integration**: May not work with all third-party tools

475

476

### Best Practices

477

478

```python

479

# Use feature detection

480

import strawberry.experimental.pydantic

481

482

if hasattr(strawberry.experimental.pydantic, 'type'):

483

# Use Pydantic integration

484

@strawberry.experimental.pydantic.type(UserModel)

485

class User:

486

pass

487

else:

488

# Fallback to regular Strawberry types

489

@strawberry.type

490

class User:

491

id: int

492

name: str

493

email: str

494

495

# Version pinning for experimental features

496

# In requirements.txt:

497

# strawberry-graphql==0.281.0 # Pin exact version for stability

498

```

499

500

### Testing Experimental Features

501

502

```python

503

import pytest

504

from pydantic import ValidationError

505

import strawberry.experimental.pydantic

506

507

def test_pydantic_integration():

508

"""Test Pydantic model integration."""

509

510

class TestModel(BaseModel):

511

name: str = Field(min_length=1)

512

age: int = Field(ge=0)

513

514

@strawberry.experimental.pydantic.type(TestModel)

515

class TestType:

516

pass

517

518

# Test successful conversion

519

valid_data = TestModel(name="Alice", age=30)

520

graphql_obj = TestType.from_pydantic(valid_data)

521

assert graphql_obj.name == "Alice"

522

assert graphql_obj.age == 30

523

524

# Test validation error handling

525

with pytest.raises(ValidationError):

526

TestModel(name="", age=-5) # Invalid data

527

528

def test_experimental_feature_availability():

529

"""Test that experimental features are available."""

530

531

# Check if feature is available

532

assert hasattr(strawberry.experimental.pydantic, 'type')

533

assert hasattr(strawberry.experimental.pydantic, 'input')

534

assert hasattr(strawberry.experimental.pydantic, 'interface')

535

```