GitLab’s GraphQL API is a powerful tool, but it’s fundamentally different from REST APIs because it lets you ask for exactly what you need, and nothing more.

Let’s see it in action. Imagine you want to get a list of all open merge requests for a specific project, along with their titles and the usernames of their authors. With a REST API, you’d likely make a request to an endpoint like /projects/:id/merge_requests and then potentially make another request for each merge request to get the author’s username. This can lead to a lot of back-and-forth.

With GraphQL, you define your query – what data you want – in a single request. Here’s what that might look like:

query {
  project(fullPath: "your-group/your-project") {
    mergeRequests(state: opened) {
      nodes {
        title
        author {
          username
        }
      }
    }
  }
}

You send this query to GitLab’s GraphQL endpoint (usually https://gitlab.example.com/api/graphql). GitLab then processes this query and returns only the data you requested, structured exactly as you asked for it.

The problem this solves is over-fetching and under-fetching of data. Over-fetching is when an API returns more data than your application needs, wasting bandwidth and processing power. Under-fetching is when you need to make multiple API calls to gather all the required data, increasing latency and complexity. GraphQL elegantly solves both.

Internally, GitLab’s GraphQL API uses a schema that defines all the available data types and relationships. When you send a query, the API validates it against this schema and then executes resolvers to fetch the requested data. These resolvers are essentially functions that know how to retrieve specific pieces of information from GitLab’s underlying data stores.

The exact levers you control are the fields and arguments within your GraphQL query. You can traverse relationships (like project -> mergeRequests -> author), filter data (like state: opened), and select specific attributes (title, username). You can also use fragments to reuse selections of fields across multiple parts of your query, making it more organized and efficient.

The concept of "aliases" is incredibly useful when you need to fetch the same type of data multiple times with different arguments within a single query. For example, if you wanted to get both open and closed merge requests for the same project, you could alias them to differentiate the results:

query {
  project(fullPath: "your-group/your-project") {
    openMergeRequests: mergeRequests(state: opened) {
      nodes {
        title
      }
    }
    closedMergeRequests: mergeRequests(state: closed) {
      nodes {
        title
      }
    }
  }
}

Without aliases, the response for mergeRequests would overwrite itself, and you wouldn’t be able to distinguish between the open and closed sets. Aliases give each field a unique name in the response, allowing you to precisely target and process the data you need.

The next concept to explore is mutations, which allow you to modify data on GitLab.

Want structured learning?

Take the full Gitlab course →