Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 29 Current »

Learn useful terminology and concepts related to using the Scope AR GraphQL API.

For more information, visit https://graphql.org/.

In this article

About GraphQL

The GraphQL data query language is:

  • A specification. The spec determines the validity of the schema on the API server. The schema determines the validity of client calls.

  • Strongly typed. The schema defines an API's type system and all object relationships. See “Reference Docs”.

  • Introspective. A client can query the schema for details about the schema.

  • Hierarchical. The shape of a GraphQL call mirrors the shape of the JSON data it returns. Nested fields let you query for and receive only the data you specify in a single round trip.

  • An application layer. GraphQL is not a storage model or a database query language. The graph refers to graph structures defined in the schema, where nodes define objects and edges define relationships between objects. The API traverses and returns application data based on the schema definitions, independent of how the data is stored. See “Using Global Node IDs”.

Why Scope is using GraphQL

Scope chose GraphQL for our API v3 because it offers significantly more flexibility for our integrators. The ability to define precisely the data you want—and only the data you want—is a powerful advantage over the REST API v2 endpoints.

If you want to get a set of data with particular properties, you don’t need to wait for a REST endpoint to be created with that data. You simply need to create a query to request that data and the GraphQL API will get it for you.

GraphQL lets you replace multiple REST requests with a single call to fetch the data you specify and all data in the system is available to you.

GraphQL Terminology

The Scope GraphQL API represents an architectural and conceptual shift from the Scope REST APIs.

You will likely encounter some new terminology in the GraphQL API reference docs.

Schema

A schema defines a GraphQL API's type system and resides on the GraphQL API server. It describes the complete set of possible data that a client can access, including:

  • Allowed operations → queries and mutations.

  • Defined types → scalars, objects, enums, interfaces, unions, and input objects.

Calls from the client are validated and executed against the public schema, and a special type of query can help you introspect the schema in real-time.

For more information, see "Discovering the Scope GraphQL API" and reference docs. For other information, such as authentication and rate limit details, check out the guides.

Note that you may need to rely on both the docs and the real-time schema validation to successfully call the GraphQL API.

Field

A field is a unit of data you can retrieve from an object. As the official GraphQL docs say: "The GraphQL query language is basically about selecting fields on objects."

The official spec also says about fields:

All GraphQL operations must specify their selections down to fields which return scalar values to ensure an unambiguously shaped response.

This means that if you try to return a field that is not a scalar, schema validation will throw an error. You must add nested subfields until all fields return scalars.

Argument

An argument is a set of key-value pairs attached to a specific field. Some fields require an argument. Mutations require an input object as an argument.

Implementation

A GraphQL schema may use the term implements to define how an object inherits from an interface.

Here's a contrived example of a schema that defines interface X and object Y:

interface X {
  some_field: String!
  other_field: String!
}

type Y implements X {
  some_field: String!
  other_field: String!
  new_field: String!
}

This means object Y requires the same fields/arguments/return types that interface X does, while adding new fields specific to object Y. (The ! means the field is required.)

In the reference docs, you'll find that:

  • Each object lists the interface(s) from which it inherits under Implements.

  • Each interface lists the objects that inherit from it under Implementations.

Connection

Connections let you query related objects as part of the same call. With connections, you can use a single GraphQL call where you would have to use multiple calls to a REST API.

It's helpful to picture a graph: dots connected by lines. The dots are nodes, the lines are edges. A connection defines a relationship between nodes.

Edge

Edges represent connections between nodes. When you query a connection, you traverse its edges to get to its nodes. Every edges field has a node field and a cursor field. Cursors are used for pagination.

Node

Node is a generic term for an object. You can look up a node directly, or you can access related nodes via a connection. If you specify a node that does not return a scalar, you must include subfields until all fields return scalars.

Discovering the data the API can provide

One way to see what is available from the GraphQL API is to use the GraphQL API reference docs.

Another way is to ask it. GraphQL is introspective. This means you can ask a GraphQL schema for details about itself.

  • Query __schema to list all types defined in the schema and get details about each:

    query {
      __schema {
        types {
          name
          kind
          description
          fields {
            name
          }
        }
      }
    }
    
  • Query __type to get details about any type:

    query {
      __type(name: "User") {
        name
        kind
        description
        fields {
          name
        }
      }
    }
    
  • You can also run an introspection query of the schema via a GET request:

    curl -X "POST" "https://cms.scopear.com/api/v3/graphql" \
         -H 'Authorization: Token token=<user auth token>' \
         -H 'PrivateAccessCode: Token token=PersonalAccessToken' \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
    	"query": "query IntrospectionQuery{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}",
    	"variables": {}
    }'

    The results are in JSON, so we recommend pretty-printing them for easier reading and searching. You can use a command-line tool like jq or pipe the results into python -m json.tool for this purpose.

    Note: The introspection query is probably the only GET request you'll run in GraphQL. If you're passing a body, the GraphQL request method is POST, whether it's a query or a mutation.

In addition to manually executing introspection queries, most GraphQL client tools include features that can help you navigate the schema in real time as you design queries and mutations. The image below shows one such tool, GraphiQL, with auto-completion (left panel) and embedded documentation (right panel). See “Exploring The Graph”.

Resource limitations

The Scope GraphQL API has limitations in place to protect against excessive or abusive calls to Scope's servers.

Node limit

To pass schema validation, all GraphQL API v3 calls must meet these standards:

  • Clients must supply a first or last argument on any connection.

  • Values of first and last must be within 1-100.

  • Individual calls cannot request more than 500,000 total nodes.

Calculating nodes in a call

These two examples show how to calculate the total nodes in a call.

  1. Simple query:

    query {
      organization {
        members(first: 50) {
          edges {
            user:node {
              name
    
              licences(first: 10) {
                totalCount
                edges {
                  license:node {
                    product
                    state
                  }
                }
              }
            }
          }
        }
      }
    }

    Calculation:

    50         = 50 users
     +
    50 x 10    = 500 user licences
    
               = 550 total nodes
  2. Complex query:

    query {
      organization {
        members(first: 50) {
          edges {
            user:node {
              name
    
              groups(first: 20) {
                edges {
                  group:node {
                    name
    
                    members(first: 10) {
                      edges {
                        member:node {
                          name
                        }
                      }
                    }
                  }
                }
              }
          }
        }
    
        scenarios(first: 10) {
          edges {
            scenario:node {
              name
            }
          }
        }
      }
    }

    Calculation:

    50            = 50 members (users)
     +
    50 x 20       = 1,000 groups
     +
    50 x 20 x 10  = 10,000 group members (users)
     +
    10             = 10 scenarios
    
                   = 10,010 total nodes

Rate limit

The GraphQL API v3 limit is different from the REST API v3's rate limits.

Why are the API rate limits different? With GraphQL, one GraphQL call can replace multiple REST calls. A single complex GraphQL call could be the equivalent of thousands of REST requests. While a single GraphQL call would fall well below the REST API rate limit, the query might be just as expensive for Scope's servers to compute.

To accurately represent the server cost of a query, the GraphQL API v3 calculates a call's rate limit score based on a normalized scale of points. A query's score factors in first and last arguments on a parent connection and its children.

  • The formula uses the first and last arguments on a parent connection and its children to pre-calculate the potential load on Scope's systems, such as MySQL, ElasticSearch, and Git.

  • Each new connection has its own point value. Points are combined with other points from the call into an overall rate limit score.

The GraphQL API v3 rate limit is 5,000 points per hour.

For Scope Apps or OAuth Apps that belong to a Scope Enterprise Cloud account, requests to resources owned by the same Scope Enterprise Cloud account have an increased limit of 15,000 points per hour.

Note that 5,000 points per hour is not the same as 5,000 calls per hour: the GraphQL API v3 and REST API v3 use different rate limits.

Note: The current formula and rate limit are subject to change as we observe how developers use the GraphQL API v3.

Returning a call's rate limit status

With the REST API v2, you can check the rate limit status by inspecting the returned HTTP headers.

With the GraphQL API v3, you can check the rate limit status by querying fields on the rateLimit object:

query {
  viewer {
    login
  }
  rateLimit {
    limit
    cost
    remaining
    resetAt
  }
}
  • The limit field returns the maximum number of points the client is permitted to consume in a 60-minute window.

  • The cost field returns the point cost for the current call that counts against the rate limit.

  • The remaining field returns the number of points remaining in the current rate limit window.)

  • The resetAt field returns the time at which the current rate limit window resets in UTC epoch seconds.

Calculating a rate limit score before running the call

Querying the rateLimit object returns a call's score, but running the call counts against the limit. To avoid this dilemma, you can calculate the score of a call before you run it. The following calculation works out to roughly the same cost that rateLimit { cost } returns.

  1. Add up the number of requests needed to fulfill each unique connection in the call. Assume every request will reach the first or last argument limits.

  2. Divide the number by 100 and round the result up to get the final aggregate cost. This step normalizes large numbers.

Note: The minimum cost of a call to the GraphQL API v3 is 1, representing a single request.

Here's an example query and score calculation:

query {
  organization {
    members(first: 50) {
      edges {
        user:node {
          name

          groups(first: 20) {
            edges {
              group:node {
                name

                members(first: 10) {
                  edges {
                    member:node {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

This query requires up to 1,011 requests to fulfill:

  • Although we're returning 50 organization members, the API has to connect to the viewer's account once to get the organization and list of users. So, requests for organization + members list = 1

  • Although we're returning 20 groups, the API has to connect to each of the 50 users to get the list of issues. So, requests for issues = 50

  • Although we're returning 10 group members, the API has to connect to each of the 200 potential total groups (50x20) to get the list of group members. So, requests for group members = 2,000

  • Total = 2,051

Dividing by 100 and rounding up gives us the final score of the query: 21

Further reading

See “Reference Docs” to view reference documentation and learn about the data types available in the GraphQL API public schema. 

See “Guides” to learn how to use the Scope GraphQL Explorer to interact with the Scope GraphQL API on real data and leverage the Scope GraphQL API for a variety of tasks.

  • No labels