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

fields-resolvers.mddocs/

0

# Field Definitions and Resolvers

1

2

Field definition system with custom resolvers, descriptions, permissions, and advanced configuration options for GraphQL fields. This system allows fine-grained control over how GraphQL fields are resolved and secured.

3

4

## Capabilities

5

6

### Field Decorator

7

8

Defines GraphQL fields with custom resolvers and configuration options.

9

10

```python { .api }

11

def field(

12

resolver: Callable = None,

13

*,

14

name: str = None,

15

description: str = None,

16

deprecation_reason: str = None,

17

permission_classes: List[Type[BasePermission]] = None,

18

extensions: List[FieldExtension] = None,

19

default: Any = dataclasses.NOTHING,

20

default_factory: Callable = dataclasses.NOTHING

21

) -> Any:

22

"""

23

Decorator to define GraphQL fields with custom configuration.

24

25

Args:

26

resolver: Custom resolver function for the field

27

name: Custom field name (defaults to function/attribute name)

28

description: Field description for GraphQL schema

29

deprecation_reason: Deprecation message if field is deprecated

30

permission_classes: List of permission classes for field-level authorization

31

extensions: List of field extensions to apply

32

default: Default value for the field

33

default_factory: Factory function for default value

34

35

Returns:

36

Configured GraphQL field

37

"""

38

```

39

40

**Usage Examples:**

41

42

```python

43

@strawberry.type

44

class User:

45

id: strawberry.ID

46

name: str

47

email: str = strawberry.field(description="User's email address")

48

49

@strawberry.field(description="User's full display name")

50

def display_name(self) -> str:

51

return f"{self.name} <{self.email}>"

52

53

@strawberry.field(

54

description="User's posts",

55

permission_classes=[IsAuthenticated]

56

)

57

def posts(self, info: strawberry.Info) -> List[Post]:

58

return get_user_posts(self.id, info.context.user)

59

60

@strawberry.field(deprecation_reason="Use display_name instead")

61

def full_name(self) -> str:

62

return self.display_name()

63

64

# Field with resolver arguments

65

@strawberry.field

66

def posts_by_tag(self, tag: str, limit: int = 10) -> List[Post]:

67

return get_posts_by_tag(self.id, tag, limit)

68

```

69

70

### Mutation Decorator

71

72

Defines GraphQL mutation fields for data modification operations.

73

74

```python { .api }

75

def mutation(

76

resolver: Callable = None,

77

*,

78

name: str = None,

79

description: str = None,

80

permission_classes: List[Type[BasePermission]] = None,

81

extensions: List[FieldExtension] = None

82

) -> Any:

83

"""

84

Decorator to define GraphQL mutation fields.

85

86

Args:

87

resolver: Resolver function for the mutation

88

name: Custom mutation name

89

description: Mutation description

90

permission_classes: Permission classes for authorization

91

extensions: Field extensions to apply

92

93

Returns:

94

Configured GraphQL mutation field

95

"""

96

```

97

98

**Usage Example:**

99

100

```python

101

@strawberry.type

102

class Mutation:

103

@strawberry.mutation(

104

description="Create a new user account",

105

permission_classes=[IsAdmin]

106

)

107

def create_user(

108

self,

109

name: str,

110

email: str,

111

age: int = 18

112

) -> User:

113

# Validation and user creation logic

114

if not email or "@" not in email:

115

raise ValueError("Invalid email address")

116

117

return User(

118

id=generate_id(),

119

name=name,

120

email=email,

121

age=age

122

)

123

124

@strawberry.mutation

125

def update_user(

126

self,

127

id: strawberry.ID,

128

input: UpdateUserInput

129

) -> User:

130

user = get_user(id)

131

if input.name is not strawberry.UNSET:

132

user.name = input.name

133

if input.email is not strawberry.UNSET:

134

user.email = input.email

135

save_user(user)

136

return user

137

```

138

139

### Subscription Decorator

140

141

Defines GraphQL subscription fields for real-time data streaming.

142

143

```python { .api }

144

def subscription(

145

resolver: Callable = None,

146

*,

147

name: str = None,

148

description: str = None,

149

permission_classes: List[Type[BasePermission]] = None,

150

extensions: List[FieldExtension] = None

151

) -> Any:

152

"""

153

Decorator to define GraphQL subscription fields.

154

155

Args:

156

resolver: Async generator function for the subscription

157

name: Custom subscription name

158

description: Subscription description

159

permission_classes: Permission classes for authorization

160

extensions: Field extensions to apply

161

162

Returns:

163

Configured GraphQL subscription field

164

"""

165

```

166

167

**Usage Example:**

168

169

```python

170

@strawberry.type

171

class Subscription:

172

@strawberry.subscription(description="Real-time user updates")

173

async def user_updates(self, user_id: strawberry.ID) -> AsyncIterator[User]:

174

# Subscribe to user updates from message broker/database

175

async for update in subscribe_to_user_updates(user_id):

176

yield User(**update)

177

178

@strawberry.subscription

179

async def message_stream(

180

self,

181

room_id: strawberry.ID,

182

info: strawberry.Info

183

) -> AsyncIterator[Message]:

184

# Check permissions

185

if not can_access_room(info.context.user, room_id):

186

raise PermissionError("Access denied")

187

188

async for message in message_broker.subscribe(f"room:{room_id}"):

189

yield Message(**message)

190

```

191

192

### Argument Definition

193

194

Defines GraphQL field arguments with validation and default values.

195

196

```python { .api }

197

def argument(

198

name: str = None,

199

description: str = None,

200

default: Any = dataclasses.NOTHING,

201

default_factory: Callable = dataclasses.NOTHING

202

) -> Any:

203

"""

204

Define GraphQL field arguments with metadata.

205

206

Args:

207

name: Custom argument name (defaults to parameter name)

208

description: Argument description

209

default: Default value for the argument

210

default_factory: Factory function for default value

211

212

Returns:

213

Configured GraphQL argument

214

"""

215

```

216

217

**Usage Example:**

218

219

```python

220

@strawberry.type

221

class Query:

222

@strawberry.field

223

def users(

224

self,

225

limit: int = strawberry.argument(

226

default=10,

227

description="Maximum number of users to return"

228

),

229

offset: int = strawberry.argument(

230

default=0,

231

description="Number of users to skip"

232

),

233

filter_name: str = strawberry.argument(

234

default=None,

235

description="Filter users by name (case-insensitive)"

236

)

237

) -> List[User]:

238

return get_users(limit=limit, offset=offset, name_filter=filter_name)

239

```

240

241

## Permission System

242

243

### Base Permission Class

244

245

```python { .api }

246

class BasePermission:

247

"""Base class for field-level permissions."""

248

249

message: str = "Permission denied"

250

251

def has_permission(

252

self,

253

source: Any,

254

info: Info,

255

**kwargs

256

) -> bool:

257

"""

258

Synchronous permission check.

259

260

Args:

261

source: Parent object being resolved

262

info: GraphQL execution info

263

**kwargs: Field arguments

264

265

Returns:

266

True if permission granted, False otherwise

267

"""

268

269

async def has_permission_async(

270

self,

271

source: Any,

272

info: Info,

273

**kwargs

274

) -> bool:

275

"""

276

Asynchronous permission check.

277

278

Args:

279

source: Parent object being resolved

280

info: GraphQL execution info

281

**kwargs: Field arguments

282

283

Returns:

284

True if permission granted, False otherwise

285

"""

286

```

287

288

**Usage Example:**

289

290

```python

291

class IsAuthenticated(strawberry.BasePermission):

292

message = "You must be authenticated to access this field"

293

294

def has_permission(self, source: Any, info: strawberry.Info, **kwargs) -> bool:

295

return info.context.user is not None

296

297

class IsOwner(strawberry.BasePermission):

298

message = "You can only access your own data"

299

300

def has_permission(self, source: Any, info: strawberry.Info, **kwargs) -> bool:

301

return info.context.user and source.user_id == info.context.user.id

302

303

class IsAdmin(strawberry.BasePermission):

304

message = "Admin access required"

305

306

async def has_permission_async(self, source: Any, info: strawberry.Info, **kwargs) -> bool:

307

user = info.context.user

308

if not user:

309

return False

310

311

# Async database check

312

return await is_user_admin(user.id)

313

314

@strawberry.type

315

class User:

316

id: strawberry.ID

317

name: str

318

319

@strawberry.field(permission_classes=[IsAuthenticated, IsOwner])

320

def email(self) -> str:

321

return self._email

322

323

@strawberry.field(permission_classes=[IsAdmin])

324

def admin_notes(self) -> str:

325

return self._admin_notes

326

```

327

328

## Resolver Context

329

330

### Info Object

331

332

The Info object provides context and metadata to resolvers.

333

334

```python { .api }

335

class Info:

336

"""GraphQL execution info passed to resolvers."""

337

338

context: Any # Request context (user, database connections, etc.)

339

field_name: str # Name of the current field being resolved

340

field_nodes: List[FieldNode] # AST nodes for the field

341

is_subscription: bool # Whether this is a subscription field

342

operation_name: str # Name of the GraphQL operation

343

path: List[Union[str, int]] # Path to this field in the result

344

return_type: Type # Expected return type of the field

345

root_value: Any # Root value passed to the schema

346

schema: Schema # GraphQL schema instance

347

variable_values: Dict[str, Any] # Variables passed to the operation

348

```

349

350

**Usage Example:**

351

352

```python

353

@strawberry.type

354

class Query:

355

@strawberry.field

356

def current_user(self, info: strawberry.Info) -> Optional[User]:

357

# Access request context

358

return info.context.user

359

360

@strawberry.field

361

def field_info(self, info: strawberry.Info) -> str:

362

return f"Resolving field '{info.field_name}' at path {info.path}"

363

364

@strawberry.field

365

def debug_info(self, info: strawberry.Info) -> Dict[str, Any]:

366

return {

367

"operation_name": info.operation_name,

368

"variables": info.variable_values,

369

"is_subscription": info.is_subscription

370

}

371

```

372

373

### Parent Object Access

374

375

```python { .api }

376

Parent: TypeVar # Type hint for parent object in resolvers

377

```

378

379

**Usage Example:**

380

381

```python

382

@strawberry.type

383

class Post:

384

id: strawberry.ID

385

title: str

386

author_id: strawberry.ID

387

388

@strawberry.field

389

def author(self) -> User:

390

# 'self' is the parent Post object

391

return get_user(self.author_id)

392

393

@strawberry.type

394

class User:

395

id: strawberry.ID

396

name: str

397

398

@strawberry.field

399

def posts(self, parent: strawberry.Parent[User]) -> List[Post]:

400

# Explicit parent type annotation

401

return get_posts_by_author(parent.id)

402

```

403

404

## Advanced Field Configuration

405

406

### Field Extensions

407

408

Field extensions allow custom logic to be applied to individual fields.

409

410

```python { .api }

411

class FieldExtension:

412

"""Base class for field-level extensions."""

413

414

def apply(self, field: StrawberryField) -> StrawberryField:

415

"""Apply extension logic to a field."""

416

```

417

418

### Default Values and Factories

419

420

```python { .api }

421

@strawberry.type

422

class User:

423

id: strawberry.ID

424

name: str

425

created_at: datetime = strawberry.field(

426

default_factory=datetime.utcnow,

427

description="Account creation timestamp"

428

)

429

is_active: bool = strawberry.field(

430

default=True,

431

description="Whether the user account is active"

432

)

433

preferences: Dict[str, Any] = strawberry.field(

434

default_factory=dict,

435

description="User preferences and settings"

436

)

437

```

438

439

### Complex Resolver Examples

440

441

```python

442

@strawberry.type

443

class Query:

444

@strawberry.field

445

async def search_users(

446

self,

447

query: str,

448

limit: int = 20,

449

info: strawberry.Info

450

) -> List[User]:

451

"""Async resolver with database operations."""

452

async with info.context.database.transaction():

453

results = await search_users_in_database(

454

query=query,

455

limit=limit,

456

user_context=info.context.user

457

)

458

return [User(**user_data) for user_data in results]

459

460

@strawberry.field

461

def paginated_posts(

462

self,

463

after: str = None,

464

first: int = 10

465

) -> PostConnection:

466

"""Cursor-based pagination resolver."""

467

posts = get_posts_after_cursor(after, first + 1) # Get one extra

468

469

edges = [

470

PostEdge(node=post, cursor=encode_cursor(post.id))

471

for post in posts[:first]

472

]

473

474

return PostConnection(

475

edges=edges,

476

page_info=PageInfo(

477

has_next_page=len(posts) > first,

478

end_cursor=edges[-1].cursor if edges else None

479

)

480

)

481

```