---
title: GraphQL
icon: Braces
description: Call GraphQL APIs from an Earl template.
---
The GraphQL protocol sends queries and mutations to a GraphQL endpoint over HTTP POST.
## A complete example
Here is a template that fetches basic info about a GitHub repository:
```hcl
version = 1
provider = "github"
categories = ["scm"]
command "get_repo" {
title = "Get repository"
summary = "Fetch basic info about a GitHub repository"
description = "Returns the star count, description, and primary language for a repository."
annotations {
mode = "read"
secrets = ["github.token"]
}
param "owner" {
type = "string"
required = true
description = "Repository owner (user or org)"
}
param "repo" {
type = "string"
required = true
description = "Repository name"
}
operation {
protocol = "graphql"
url = "https://api.github.com/graphql"
auth {
kind = "bearer"
secret = "github.token"
}
graphql {
query = <<-GQL
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
stargazerCount
description
primaryLanguage { name }
}
}
GQL
variables = {
owner = "{{ args.owner }}"
repo = "{{ args.repo }}"
}
}
}
result {
decode = "json"
output = "{{ result.data.repository.stargazerCount }} stars — {{ result.data.repository.description | default('no description') }}"
}
}
```
Store your token and run it:
```bash
earl secrets set github.token
earl call github.get_repo --owner torvalds --repo linux
```
## Walk-through
### operation
```hcl
operation {
protocol = "graphql"
url = "https://api.github.com/graphql"
...
}
```
`protocol = "graphql"` and `url` are the only required fields. GraphQL requests default to HTTP POST. A `method` field exists but is rarely needed — only set it if the server requires `GET` instead of the standard `POST`.
For the full list of auth kinds and OAuth2 profile setup, see [Secrets & Auth](/docs/secrets-and-auth).
### graphql block
```hcl
graphql {
query = <<-GQL
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
stargazerCount
description
primaryLanguage { name }
}
}
GQL
variables = {
owner = "{{ args.owner }}"
repo = "{{ args.repo }}"
}
}
```
`query` holds the GQL document. The heredoc syntax (`<<-GQL ... GQL`) keeps multi-line queries readable without escaping.
`variables` is a map whose keys must match the `$variable` declarations in the query. Values support Jinja expressions — `"{{ args.owner }}"` is rendered before the request is sent.
A third field, `operation_name`, is optional. Use it only when the document contains multiple named operations and you need to tell the server which one to execute.
### result
```hcl
result {
decode = "json"
output = "{{ result.data.repository.stargazerCount }} stars — {{ result.data.repository.description | default('no description') }}"
}
```
The GraphQL response is a JSON envelope. `decode = "json"` parses it and makes the full object available as `result`. Successful data is at `result.data.<field>`. If the server returns errors, they appear at `result.errors`.
## Mutations
Mutations work the same way. Use the `mutation` keyword in the GQL document and set `mode = "write"` in annotations.
```hcl
command "add_star" {
title = "Star repository"
summary = "Add a star to a GitHub repository"
description = "Stars a repository on behalf of the authenticated user."
annotations {
mode = "write"
secrets = ["github.token"]
}
param "repo_id" {
type = "string"
required = true
description = "GraphQL node ID of the repository"
}
operation {
protocol = "graphql"
url = "https://api.github.com/graphql"
auth {
kind = "bearer"
secret = "github.token"
}
graphql {
query = <<-GQL
mutation($id: ID!) {
addStar(input: { starrableId: $id }) {
starrable { stargazerCount }
}
}
GQL
variables = {
id = "{{ args.repo_id }}"
}
}
}
result {
decode = "json"
output = "Starred. New star count: {{ result.data.addStar.starrable.stargazerCount }}"
}
}
```
To switch between production and staging endpoints, see [Environments](/docs/environments).
For naming conventions, secret declarations, and other patterns that apply across all protocols, see [Best Practices](/docs/best-practices).
For the full field reference, see [Template Schema — GraphQL](/docs/template-schema#graphql).