or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cicd.mdclient-auth.mdgraphql.mdindex.mdissues-mrs.mdprojects.mdrepository.mdusers-groups.md

graphql.mddocs/

0

# GraphQL Clients

1

2

Advanced GraphQL query execution with both synchronous and asynchronous clients. Provides flexible querying capabilities and efficient data fetching for complex GitLab API interactions using GitLab's GraphQL API.

3

4

## Capabilities

5

6

### Synchronous GraphQL Client

7

8

```python { .api }

9

class GraphQL:

10

"""

11

Synchronous GraphQL client for GitLab API queries.

12

13

Provides GraphQL query execution with retry logic, error handling,

14

and response processing for GitLab's GraphQL API endpoint.

15

"""

16

17

def __init__(

18

self,

19

url: str,

20

auth: tuple[str, str] | None = None,

21

timeout: float | None = None,

22

retry_transient_errors: bool = False,

23

**kwargs

24

) -> None:

25

"""

26

Initialize GraphQL client.

27

28

Parameters:

29

- url: GitLab GraphQL endpoint URL

30

- auth: Authentication tuple (token_type, token)

31

- timeout: Request timeout in seconds

32

- retry_transient_errors: Retry on transient errors

33

"""

34

35

def execute(

36

self,

37

query: str,

38

variables: dict | None = None,

39

**kwargs

40

) -> dict:

41

"""

42

Execute GraphQL query.

43

44

Parameters:

45

- query: GraphQL query string

46

- variables: Query variables dictionary

47

48

Returns:

49

Dictionary with query results

50

51

Raises:

52

GitlabGraphQLError: If query execution fails

53

54

Example:

55

```python

56

query = '''

57

query getProject($path: ID!) {

58

project(fullPath: $path) {

59

id

60

name

61

description

62

webUrl

63

repository {

64

tree {

65

lastCommit {

66

sha

67

message

68

authoredDate

69

}

70

}

71

}

72

issues(state: OPENED) {

73

count

74

edges {

75

node {

76

id

77

title

78

state

79

createdAt

80

}

81

}

82

}

83

}

84

}

85

'''

86

87

variables = {"path": "group/project"}

88

result = client.execute(query, variables)

89

```

90

"""

91

```

92

93

### Asynchronous GraphQL Client

94

95

```python { .api }

96

class AsyncGraphQL:

97

"""

98

Asynchronous GraphQL client for GitLab API queries.

99

100

Provides async GraphQL query execution with the same capabilities

101

as the synchronous client but with non-blocking I/O operations.

102

"""

103

104

def __init__(

105

self,

106

url: str,

107

auth: tuple[str, str] | None = None,

108

timeout: float | None = None,

109

retry_transient_errors: bool = False,

110

**kwargs

111

) -> None:

112

"""

113

Initialize async GraphQL client.

114

115

Parameters:

116

- url: GitLab GraphQL endpoint URL

117

- auth: Authentication tuple (token_type, token)

118

- timeout: Request timeout in seconds

119

- retry_transient_errors: Retry on transient errors

120

"""

121

122

async def execute(

123

self,

124

query: str,

125

variables: dict | None = None,

126

**kwargs

127

) -> dict:

128

"""

129

Execute GraphQL query asynchronously.

130

131

Parameters:

132

- query: GraphQL query string

133

- variables: Query variables dictionary

134

135

Returns:

136

Dictionary with query results

137

138

Raises:

139

GitlabGraphQLError: If query execution fails

140

141

Example:

142

```python

143

import asyncio

144

145

async def main():

146

query = '''

147

query getUserProjects($userId: UserID!) {

148

user(id: $userId) {

149

id

150

name

151

username

152

projectMemberships {

153

edges {

154

node {

155

project {

156

id

157

name

158

visibility

159

lastActivityAt

160

}

161

accessLevel {

162

integerValue

163

}

164

}

165

}

166

}

167

}

168

}

169

'''

170

171

variables = {"userId": "gid://gitlab/User/123"}

172

result = await client.execute(query, variables)

173

174

asyncio.run(main())

175

```

176

"""

177

```

178

179

### Client Creation and Configuration

180

181

```python { .api }

182

# Create GraphQL clients from Gitlab instance

183

@property

184

def graphql(self) -> GraphQL:

185

"""

186

Get synchronous GraphQL client configured for this GitLab instance.

187

188

Returns:

189

Configured GraphQL client

190

191

Example:

192

```python

193

gl = gitlab.Gitlab("https://gitlab.example.com", private_token="token")

194

gql = gl.graphql

195

196

query = "{ currentUser { id name } }"

197

result = gql.execute(query)

198

```

199

"""

200

201

@property

202

def async_graphql(self) -> AsyncGraphQL:

203

"""

204

Get asynchronous GraphQL client configured for this GitLab instance.

205

206

Returns:

207

Configured AsyncGraphQL client

208

209

Example:

210

```python

211

gl = gitlab.Gitlab("https://gitlab.example.com", private_token="token")

212

async_gql = gl.async_graphql

213

214

async def get_user():

215

query = "{ currentUser { id name } }"

216

result = await async_gql.execute(query)

217

return result

218

```

219

"""

220

```

221

222

### GraphQL Schema and Common Queries

223

224

```python { .api }

225

# Common GraphQL query patterns for GitLab

226

227

# Project Information Query

228

PROJECT_QUERY = """

229

query getProject($fullPath: ID!) {

230

project(fullPath: $fullPath) {

231

id

232

name

233

path

234

fullPath

235

description

236

visibility

237

webUrl

238

sshUrlToRepo

239

httpUrlToRepo

240

defaultBranch

241

repository {

242

exists

243

empty

244

rootRef

245

}

246

statistics {

247

commitCount

248

storageSize

249

repositorySize

250

wikiSize

251

lfsObjectsSize

252

jobArtifactsSize

253

packagesSize

254

snippetsSize

255

}

256

lastActivityAt

257

createdAt

258

updatedAt

259

}

260

}

261

"""

262

263

# Issues Query

264

ISSUES_QUERY = """

265

query getProjectIssues($fullPath: ID!, $state: IssuableState, $first: Int) {

266

project(fullPath: $fullPath) {

267

issues(state: $state, first: $first) {

268

count

269

pageInfo {

270

hasNextPage

271

hasPreviousPage

272

startCursor

273

endCursor

274

}

275

edges {

276

node {

277

id

278

iid

279

title

280

description

281

state

282

createdAt

283

updatedAt

284

closedAt

285

author {

286

id

287

name

288

username

289

avatarUrl

290

}

291

assignees {

292

edges {

293

node {

294

id

295

name

296

username

297

}

298

}

299

}

300

labels {

301

edges {

302

node {

303

id

304

title

305

color

306

description

307

}

308

}

309

}

310

milestone {

311

id

312

title

313

state

314

dueDate

315

}

316

webUrl

317

upvotes

318

downvotes

319

userNotesCount

320

confidential

321

discussionLocked

322

dueDate

323

timeEstimate

324

totalTimeSpent

325

humanTimeEstimate

326

humanTotalTimeSpent

327

}

328

}

329

}

330

}

331

}

332

"""

333

334

# Merge Requests Query

335

MERGE_REQUESTS_QUERY = """

336

query getProjectMergeRequests($fullPath: ID!, $state: MergeRequestState, $first: Int) {

337

project(fullPath: $fullPath) {

338

mergeRequests(state: $state, first: $first) {

339

count

340

pageInfo {

341

hasNextPage

342

hasPreviousPage

343

startCursor

344

endCursor

345

}

346

edges {

347

node {

348

id

349

iid

350

title

351

description

352

state

353

createdAt

354

updatedAt

355

mergedAt

356

closedAt

357

sourceBranch

358

targetBranch

359

author {

360

id

361

name

362

username

363

avatarUrl

364

}

365

assignees {

366

edges {

367

node {

368

id

369

name

370

username

371

}

372

}

373

}

374

reviewers {

375

edges {

376

node {

377

id

378

name

379

username

380

}

381

}

382

}

383

labels {

384

edges {

385

node {

386

id

387

title

388

color

389

}

390

}

391

}

392

milestone {

393

id

394

title

395

state

396

}

397

webUrl

398

upvotes

399

downvotes

400

userNotesCount

401

shouldRemoveSourceBranch

402

forceRemoveSourceBranch

403

squash

404

mergeable

405

mergeStatus

406

draft

407

workInProgress

408

conflicts

409

divergedCommitsCount

410

rebaseInProgress

411

defaultMergeCommitMessage

412

mergeCommitSha

413

squashCommitSha

414

diffHeadSha

415

diffBaseSha

416

reference

417

taskCompletionStatus {

418

count

419

completedCount

420

}

421

}

422

}

423

}

424

}

425

}

426

"""

427

428

# Users Query

429

USERS_QUERY = """

430

query getUsers($search: String, $first: Int) {

431

users(search: $search, first: $first) {

432

count

433

pageInfo {

434

hasNextPage

435

hasPreviousPage

436

startCursor

437

endCursor

438

}

439

edges {

440

node {

441

id

442

name

443

username

444

email

445

avatarUrl

446

webUrl

447

bot

448

state

449

createdAt

450

lastActivityOn

451

location

452

publicEmail

453

skype

454

linkedin

455

twitter

456

websiteUrl

457

organization

458

jobTitle

459

bio

460

workInformation

461

localTime

462

pronouns

463

bot

464

followers {

465

count

466

}

467

following {

468

count

469

}

470

groupMemberships {

471

count

472

}

473

projectMemberships {

474

count

475

}

476

starredProjects {

477

count

478

}

479

status {

480

availability

481

emoji

482

message

483

}

484

}

485

}

486

}

487

}

488

"""

489

490

# Groups Query

491

GROUPS_QUERY = """

492

query getGroups($search: String, $first: Int) {

493

groups(search: $search, first: $first) {

494

count

495

pageInfo {

496

hasNextPage

497

hasPreviousPage

498

startCursor

499

endCursor

500

}

501

edges {

502

node {

503

id

504

name

505

path

506

fullName

507

fullPath

508

description

509

visibility

510

webUrl

511

avatarUrl

512

parent {

513

id

514

name

515

fullPath

516

}

517

projects {

518

count

519

}

520

descendantGroups {

521

count

522

}

523

createdAt

524

requestAccessEnabled

525

requireTwoFactorAuthentication

526

twoFactorGracePeriod

527

autoDevopsEnabled

528

emailsDisabled

529

mentionsDisabled

530

lfsEnabled

531

shareWithGroupLock

532

projectCreationLevel

533

subgroupCreationLevel

534

defaultBranchProtection

535

repositoryStorageLimit

536

}

537

}

538

}

539

}

540

"""

541

```

542

543

### Usage Examples

544

545

```python

546

import gitlab

547

import asyncio

548

549

# Initialize GitLab client

550

gl = gitlab.Gitlab("https://gitlab.example.com", private_token="your-token")

551

552

# Synchronous GraphQL usage

553

def sync_graphql_example():

554

gql = gl.graphql

555

556

# Simple current user query

557

user_query = """

558

{

559

currentUser {

560

id

561

name

562

username

563

email

564

avatarUrl

565

}

566

}

567

"""

568

569

result = gql.execute(user_query)

570

user = result['data']['currentUser']

571

print(f"Current user: {user['name']} ({user['username']})")

572

573

# Project query with variables

574

project_query = """

575

query getProject($path: ID!) {

576

project(fullPath: $path) {

577

id

578

name

579

description

580

visibility

581

webUrl

582

issues(state: OPENED, first: 5) {

583

count

584

edges {

585

node {

586

id

587

title

588

author {

589

name

590

}

591

createdAt

592

}

593

}

594

}

595

mergeRequests(state: OPENED, first: 5) {

596

count

597

edges {

598

node {

599

id

600

title

601

author {

602

name

603

}

604

sourceBranch

605

targetBranch

606

}

607

}

608

}

609

}

610

}

611

"""

612

613

variables = {"path": "group/project-name"}

614

result = gql.execute(project_query, variables)

615

616

project = result['data']['project']

617

print(f"Project: {project['name']}")

618

print(f"Open issues: {project['issues']['count']}")

619

print(f"Open MRs: {project['mergeRequests']['count']}")

620

621

# Asynchronous GraphQL usage

622

async def async_graphql_example():

623

async_gql = gl.async_graphql

624

625

# Batch multiple queries

626

queries = [

627

{

628

"query": """

629

query getProject($path: ID!) {

630

project(fullPath: $path) {

631

id

632

name

633

statistics {

634

commitCount

635

storageSize

636

}

637

}

638

}

639

""",

640

"variables": {"path": "group/project1"}

641

},

642

{

643

"query": """

644

query getProject($path: ID!) {

645

project(fullPath: $path) {

646

id

647

name

648

statistics {

649

commitCount

650

storageSize

651

}

652

}

653

}

654

""",

655

"variables": {"path": "group/project2"}

656

}

657

]

658

659

# Execute queries concurrently

660

tasks = [

661

async_gql.execute(q["query"], q["variables"])

662

for q in queries

663

]

664

665

results = await asyncio.gather(*tasks)

666

667

for result in results:

668

project = result['data']['project']

669

stats = project['statistics']

670

print(f"{project['name']}: {stats['commitCount']} commits, {stats['storageSize']} bytes")

671

672

# Complex pagination example

673

def paginated_graphql_query():

674

gql = gl.graphql

675

676

query = """

677

query getProjectIssues($path: ID!, $after: String) {

678

project(fullPath: $path) {

679

issues(first: 50, after: $after) {

680

pageInfo {

681

hasNextPage

682

endCursor

683

}

684

edges {

685

node {

686

id

687

title

688

state

689

createdAt

690

author {

691

name

692

}

693

}

694

}

695

}

696

}

697

}

698

"""

699

700

all_issues = []

701

variables = {"path": "group/project"}

702

has_next_page = True

703

704

while has_next_page:

705

result = gql.execute(query, variables)

706

issues_data = result['data']['project']['issues']

707

708

# Collect issues

709

issues = [edge['node'] for edge in issues_data['edges']]

710

all_issues.extend(issues)

711

712

# Check for next page

713

page_info = issues_data['pageInfo']

714

has_next_page = page_info['hasNextPage']

715

716

if has_next_page:

717

variables['after'] = page_info['endCursor']

718

719

print(f"Retrieved {len(all_issues)} issues total")

720

return all_issues

721

722

# Mutation example (create issue)

723

def create_issue_mutation():

724

gql = gl.graphql

725

726

mutation = """

727

mutation createIssue($input: CreateIssueInput!) {

728

createIssue(input: $input) {

729

issue {

730

id

731

iid

732

title

733

description

734

state

735

webUrl

736

}

737

errors

738

}

739

}

740

"""

741

742

variables = {

743

"input": {

744

"projectPath": "group/project",

745

"title": "New issue via GraphQL",

746

"description": "This issue was created using GraphQL mutation",

747

"confidential": False

748

}

749

}

750

751

result = gql.execute(mutation, variables)

752

753

if result['data']['createIssue']['errors']:

754

print(f"Errors: {result['data']['createIssue']['errors']}")

755

else:

756

issue = result['data']['createIssue']['issue']

757

print(f"Created issue #{issue['iid']}: {issue['title']}")

758

print(f"URL: {issue['webUrl']}")

759

760

# Run examples

761

sync_graphql_example()

762

asyncio.run(async_graphql_example())

763

paginated_issues = paginated_graphql_query()

764

create_issue_mutation()

765

```

766

767

### Error Handling

768

769

```python { .api }

770

class GitlabGraphQLError(GitlabError):

771

"""Raised when GraphQL query execution fails."""

772

pass

773

```

774

775

Example error handling:

776

```python

777

try:

778

result = gql.execute(query, variables)

779

780

# Check for GraphQL errors in response

781

if 'errors' in result:

782

for error in result['errors']:

783

print(f"GraphQL Error: {error['message']}")

784

if 'locations' in error:

785

print(f"Location: {error['locations']}")

786

else:

787

# Process successful result

788

data = result['data']

789

790

except gitlab.GitlabGraphQLError as e:

791

print(f"GraphQL execution failed: {e}")

792

except gitlab.GitlabAuthenticationError:

793

print("Authentication failed - check your token")

794

except gitlab.GitlabConnectionError:

795

print("Connection failed - check your URL")

796

```

797

798

### GraphQL vs REST API

799

800

GraphQL provides several advantages over the REST API:

801

802

- **Single Request**: Fetch related data in one query instead of multiple REST calls

803

- **Flexible Data Selection**: Request only the fields you need

804

- **Strong Type System**: Built-in validation and introspection

805

- **Real-time Subscriptions**: Subscribe to data changes (GitLab Premium feature)

806

- **Efficient Pagination**: Cursor-based pagination with connection patterns

807

- **Reduced Over-fetching**: Get exactly the data you need

808

809

Use GraphQL when:

810

- You need data from multiple related resources

811

- You want to minimize network requests

812

- You need complex filtering or nested data

813

- You're building data-heavy applications

814

- You want strongly-typed API interactions

815

816

Use REST API when:

817

- You need CRUD operations on single resources

818

- You're performing administrative actions

819

- You need features not yet available in GraphQL

820

- You're working with legacy systems expecting REST endpoints