17 KiB
17 KiB
GitLab GraphQL API Reference
Overview
GitLab's GraphQL API provides a more efficient alternative to REST API, allowing you to request exactly the data you need in a single query. The GraphQL API is the primary development focus for new features at GitLab.
GraphQL Endpoint
https://gitlab.com/api/graphql
For self-hosted instances:
https://your-gitlab-instance.com/api/graphql
Authentication
Personal Access Token
curl --header "Authorization: Bearer <your_token>" \
--header "Content-Type: application/json" \
--request POST \
--data '{"query": "{ currentUser { username } }"}' \
https://gitlab.com/api/graphql
In HTTP Headers
Authorization: Bearer <your_access_token>
Interactive GraphQL Explorer (GraphiQL)
Access the interactive explorer:
https://gitlab.com/-/graphql-explorer
Features:
- Auto-completion
- Schema documentation
- Query validation
- Query history
Basic Query Structure
{
currentUser {
id
username
name
email
}
}
Common Queries
Get Current User
{
currentUser {
id
username
name
email
avatarUrl
webUrl
status {
message
availability
}
}
}
Get Project Details
{
project(fullPath: "group/project") {
id
name
description
visibility
createdAt
lastActivityAt
webUrl
repository {
rootRef
empty
}
statistics {
commitCount
storageSize
repositorySize
lfsObjectsSize
}
}
}
List Projects
{
projects(first: 10, membership: true) {
nodes {
id
name
fullPath
description
visibility
createdAt
webUrl
starCount
forksCount
}
pageInfo {
hasNextPage
endCursor
}
}
}
Get Merge Requests
{
project(fullPath: "group/project") {
mergeRequests(first: 10, state: opened) {
nodes {
id
iid
title
description
state
createdAt
updatedAt
author {
username
name
}
sourceBranch
targetBranch
webUrl
draft
workInProgress
mergeStatus
approved
conflicts
diffStats {
additions
deletions
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Get Issues
{
project(fullPath: "group/project") {
issues(first: 10, state: opened) {
nodes {
id
iid
title
description
state
createdAt
closedAt
author {
username
name
}
assignees {
nodes {
username
name
}
}
labels {
nodes {
title
color
}
}
milestone {
title
dueDate
}
webUrl
upvotes
downvotes
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Get Pipeline Details
{
project(fullPath: "group/project") {
pipeline(iid: "123") {
id
iid
status
ref
createdAt
updatedAt
startedAt
finishedAt
duration
coverage
user {
username
name
}
jobs {
nodes {
id
name
stage
status
createdAt
startedAt
finishedAt
duration
webPath
}
}
}
}
}
Get Commits
{
project(fullPath: "group/project") {
repository {
tree(ref: "main") {
lastCommit {
id
sha
title
message
authoredDate
author {
name
email
}
}
}
}
}
}
Get Group Details
{
group(fullPath: "group-name") {
id
name
fullPath
description
visibility
webUrl
projects(first: 10) {
nodes {
name
fullPath
}
}
members {
nodes {
user {
username
name
}
accessLevel {
integerValue
stringValue
}
}
}
}
}
Mutations
Create Issue
mutation {
createIssue(input: {
projectPath: "group/project"
title: "New issue title"
description: "Issue description"
assigneeIds: ["gid://gitlab/User/1"]
labelIds: ["gid://gitlab/Label/1"]
}) {
issue {
id
iid
title
webUrl
}
errors
}
}
Update Issue
mutation {
updateIssue(input: {
projectPath: "group/project"
iid: "123"
title: "Updated title"
description: "Updated description"
stateEvent: CLOSE
}) {
issue {
id
title
state
}
errors
}
}
Create Merge Request
mutation {
mergeRequestCreate(input: {
projectPath: "group/project"
title: "New feature"
description: "This MR adds..."
sourceBranch: "feature-branch"
targetBranch: "main"
}) {
mergeRequest {
id
iid
title
webUrl
}
errors
}
}
Update Merge Request
mutation {
mergeRequestUpdate(input: {
projectPath: "group/project"
iid: "123"
title: "Updated title"
description: "Updated description"
assigneeIds: ["gid://gitlab/User/1"]
}) {
mergeRequest {
id
title
state
}
errors
}
}
Merge Merge Request
mutation {
mergeRequestMerge(input: {
projectPath: "group/project"
iid: "123"
squash: true
}) {
mergeRequest {
id
state
mergedAt
}
errors
}
}
Create Note (Comment)
mutation {
createNote(input: {
noteableId: "gid://gitlab/Issue/123"
body: "This is a comment"
}) {
note {
id
body
author {
username
}
}
errors
}
}
Update Project
mutation {
updateProject(input: {
projectPath: "group/project"
description: "New description"
visibility: "public"
}) {
project {
id
description
visibility
}
errors
}
}
Create Branch
mutation {
createBranch(input: {
projectPath: "group/project"
name: "new-branch"
ref: "main"
}) {
branch {
name
commit {
id
}
}
errors
}
}
Create Snippet
mutation {
createSnippet(input: {
projectPath: "group/project"
title: "Code snippet"
fileName: "example.rb"
content: "puts 'Hello World'"
visibility: "private"
}) {
snippet {
id
title
webUrl
}
errors
}
}
Run Pipeline
mutation {
pipelineCreate(input: {
projectPath: "group/project"
ref: "main"
}) {
pipeline {
id
status
}
errors
}
}
Cancel Pipeline
mutation {
pipelineCancel(input: {
id: "gid://gitlab/Ci::Pipeline/123"
}) {
pipeline {
id
status
}
errors
}
}
Variables and Fragments
Using Variables
query GetProject($fullPath: ID!) {
project(fullPath: $fullPath) {
id
name
description
}
}
Variables:
{
"fullPath": "group/project"
}
Using Fragments
fragment UserInfo on User {
id
username
name
email
avatarUrl
}
query {
currentUser {
...UserInfo
}
project(fullPath: "group/project") {
author {
...UserInfo
}
}
}
Pagination
Cursor-Based Pagination
query {
projects(first: 10, after: "cursor_value") {
nodes {
id
name
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
Complete Pagination Example
import requests
import json
def fetch_all_projects(token):
url = "https://gitlab.com/api/graphql"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
has_next_page = True
after_cursor = None
all_projects = []
while has_next_page:
query = """
query($after: String) {
projects(first: 10, after: $after, membership: true) {
nodes {
id
name
fullPath
}
pageInfo {
hasNextPage
endCursor
}
}
}
"""
variables = {"after": after_cursor}
response = requests.post(
url,
headers=headers,
json={"query": query, "variables": variables}
)
data = response.json()["data"]
projects = data["projects"]
all_projects.extend(projects["nodes"])
has_next_page = projects["pageInfo"]["hasNextPage"]
after_cursor = projects["pageInfo"]["endCursor"]
return all_projects
Introspection
Query Schema
{
__schema {
types {
name
description
}
}
}
Query Type Details
{
__type(name: "Project") {
name
fields {
name
type {
name
kind
}
}
}
}
Advanced Queries
Nested Relationships
{
group(fullPath: "group-name") {
projects(first: 5) {
nodes {
name
mergeRequests(first: 3, state: opened) {
nodes {
title
author {
username
}
assignees {
nodes {
username
}
}
approvedBy {
nodes {
username
}
}
}
}
}
}
}
}
Conditional Fields
query GetProject($includeMergeRequests: Boolean!) {
project(fullPath: "group/project") {
id
name
mergeRequests(first: 10) @include(if: $includeMergeRequests) {
nodes {
title
}
}
}
}
Aliases
{
openIssues: project(fullPath: "group/project") {
issues(state: opened) {
count
}
}
closedIssues: project(fullPath: "group/project") {
issues(state: closed) {
count
}
}
}
Client Libraries
Python (gql)
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
transport = RequestsHTTPTransport(
url="https://gitlab.com/api/graphql",
headers={"Authorization": f"Bearer {token}"},
)
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql("""
query {
currentUser {
username
name
}
}
""")
result = client.execute(query)
print(result)
JavaScript (Apollo Client)
import { ApolloClient, InMemoryCache, gql, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: 'https://gitlab.com/api/graphql',
});
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: `Bearer ${token}`,
}
}
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
const GET_CURRENT_USER = gql`
query {
currentUser {
username
name
}
}
`;
client.query({ query: GET_CURRENT_USER })
.then(result => console.log(result));
Ruby (graphql-client)
require "graphql/client"
require "graphql/client/http"
module GitLabAPI
HTTP = GraphQL::Client::HTTP.new("https://gitlab.com/api/graphql") do
def headers(context)
{ "Authorization" => "Bearer #{ENV['GITLAB_TOKEN']}" }
end
end
Schema = GraphQL::Client.load_schema(HTTP)
Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end
CurrentUserQuery = GitLabAPI::Client.parse <<-'GRAPHQL'
query {
currentUser {
username
name
}
}
GRAPHQL
result = GitLabAPI::Client.query(CurrentUserQuery)
puts result.data.current_user.username
Rate Limiting
GraphQL queries count towards API rate limits based on query complexity.
Check rate limit:
{
currentUser {
username
}
}
Response headers:
RateLimit-Limit: Total allowedRateLimit-Remaining: Remaining requestsRateLimit-Reset: Reset timestamp
Error Handling
Error Response Format
{
"data": null,
"errors": [
{
"message": "Error message",
"path": ["field", "path"],
"extensions": {
"code": "ERROR_CODE"
}
}
]
}
Common Error Types
NOT_FOUND: Resource not foundUNAUTHORIZED: Authentication requiredFORBIDDEN: Insufficient permissionsVALIDATION_ERROR: Invalid inputINTERNAL_SERVER_ERROR: Server error
Error Handling Example
def execute_query(query, variables=None):
response = requests.post(
"https://gitlab.com/api/graphql",
headers={"Authorization": f"Bearer {token}"},
json={"query": query, "variables": variables}
)
result = response.json()
if "errors" in result:
for error in result["errors"]:
print(f"Error: {error['message']}")
if "path" in error:
print(f"Path: {error['path']}")
return None
return result["data"]
Best Practices
1. Request Only Needed Fields
Bad:
{
project(fullPath: "group/project") {
id
name
description
# ... all fields
}
}
Good:
{
project(fullPath: "group/project") {
id
name
}
}
2. Use Fragments for Reusability
fragment IssueFields on Issue {
id
iid
title
state
author {
username
}
}
query {
project(fullPath: "group/project") {
openIssues: issues(state: opened) {
nodes {
...IssueFields
}
}
closedIssues: issues(state: closed) {
nodes {
...IssueFields
}
}
}
}
3. Implement Pagination
Always use pagination for large datasets:
{
projects(first: 100, after: $cursor) {
nodes { ... }
pageInfo {
hasNextPage
endCursor
}
}
}
4. Handle Errors Gracefully
async function fetchData() {
try {
const result = await client.query({ query: MY_QUERY });
return result.data;
} catch (error) {
if (error.graphQLErrors) {
error.graphQLErrors.forEach(({ message, path }) => {
console.error(`GraphQL error: ${message} at ${path}`);
});
}
if (error.networkError) {
console.error(`Network error: ${error.networkError}`);
}
}
}
5. Use Variables
query GetProject($path: ID!, $mrCount: Int!) {
project(fullPath: $path) {
mergeRequests(first: $mrCount) {
nodes { title }
}
}
}
6. Optimize Query Depth
Limit nested query depth to avoid performance issues:
# Avoid deep nesting
{
group {
projects {
mergeRequests {
commits {
notes {
# Too deep!
}
}
}
}
}
}
Useful Queries for Common Tasks
Search Across Projects
{
projects(search: "keyword", first: 10) {
nodes {
name
fullPath
description
}
}
}
Get User Activity
{
user(username: "john.doe") {
assignedMergeRequests(first: 10) {
nodes {
title
project {
name
}
}
}
authoredMergeRequests(first: 10) {
nodes {
title
state
}
}
}
}
Get Deployment Status
{
project(fullPath: "group/project") {
environments {
nodes {
name
state
lastDeployment {
status
createdAt
deployable {
... on Job {
name
}
}
}
}
}
}
}
Get Security Vulnerabilities
{
project(fullPath: "group/project") {
vulnerabilities {
nodes {
id
title
severity
state
reportType
detectedAt
}
}
}
}
Migration from REST to GraphQL
REST to GraphQL Comparison
REST: Get project and its merge requests
# 2 requests
curl "https://gitlab.com/api/v4/projects/123"
curl "https://gitlab.com/api/v4/projects/123/merge_requests"
GraphQL: Single request
{
project(fullPath: "group/project") {
id
name
mergeRequests(first: 10) {
nodes {
title
}
}
}
}
Subscriptions (Real-time Updates)
GitLab supports GraphQL subscriptions for real-time updates:
subscription {
issuableUpdated {
issue {
id
title
state
}
}
}
Additional Resources
- GraphQL API Documentation: https://docs.gitlab.com/ee/api/graphql/
- GraphQL Explorer: https://gitlab.com/-/graphql-explorer
- GraphQL Reference: https://docs.gitlab.com/ee/api/graphql/reference/
- Best Practices: https://docs.gitlab.com/ee/api/graphql/#best-practices