Docs
Launch GraphOS Studio

Measuring field usage with GraphOS Studio

Understand which fields your clients use


For details on setting up metrics reporting to Studio, see Reporting field usage.

In GraphOS Studio, your graph's Fields page can display a table of basic usage metrics for each in your :

Fields page in Studio

You can click the name of any in the table to open its Field Insights page. This page displays in-depth details about which clients and s contribute to the 's usage, along with performance metrics:

Field Insights page

Field executions vs. requesting operations

The table on the s page displays some combination of the following metrics for each , depending on which data you report to :

MetricDescription
Field executionsHow many times your servers have executed the resolver for the field over a given time period.
Requested by operationsHow many operations sent by clients over a given time period have included the field, according to metrics provided by your servers.

For each of these columns to appear on the s page, your GraphQL servers must report the associated metrics to GraphOS. If some but not all of your GraphQL servers report this data, the s page presents an incomplete picture of your graph's usage.

Let's look at these two metrics for s of an example type:

Fields page in Studio

As you can see, the daily field executions and requesting operations for a single can differ significantly! There are many possible reasons for this, described below.

Objects in lists

Let's say a client executes the following query one time:

query GetBooks {
books {
title
}
}

If Query.books returns a list of ten Book objects, then Book.title is resolved ten times. This query therefore contributes ten field executions and just one requesting operation to Book.title.

Multiple references to a field

Let's say a client executes the following query one time:

query GetTwoBooks {
firstBook: book(id: "123") {
title
}
secondBook: book(id: "345") {
title
}
}

This includes two references to the s Query.book and Book.title. Therefore, the s for these s each execute twice (assuming Query.book doesn't return null). However, these multiple references are all part of a single .

Therefore, this query contributes two field executions and just one requesting operation to each of Query.book and Book.title.

Fields that return interfaces

Let's say our GraphQL server's defines the following interface and s:

interface Media {
title: String!
}
type Book implements Media {
title: String!
author: String!
}
type Query {
favoriteMedia: Media!
}

Now, let's say a client executes the following query:

query GetFavoriteMedia {
favoriteMedia {
title
}
}

If Query.favoriteMedia returns a Book object here, then Book.title is resolved one time. However, the original query does not reference Book.title. Instead, it references Media.title, because Query.favoriteMedia has a return type of Media.

Therefore, this query contributes one field execution and zero requesting operations to Book.title. It also contributes one requesting operation to Media.title. Note that interface s always have zero field executions.

Requested fields that aren't resolved

Let's say a client executes the following query one time:

query GetLoggedInUser {
loggedInUser {
name
}
}

Now, let's say Query.loggedInUser returns null because no user is logged in. In this case, the for User.name never executes, because its parent retuns null. Therefore, this query contributes zero field executions and one requesting operation to User.name.

A requested might not be resolved for any of these reasons:

  • The is nested under a that returns null, as shown above.
  • The is nested under a that returns a list, but the list is empty.
  • The is part of a that doesn't apply to a particular object.
  • The is skipped due to a @skip or @include .

@key and @requires fields in a federated graph

This case applies only to graphs that use Apollo Federation.

Let's say our federated graph includes these two s:

# Products subgraph
type Product @key(fields: "id") {
id: ID!
name: String!
}
# Reviews subgraph
extend type Product @key(fields: "id") {
id: ID! @external
}
type Review {
id: ID!
score: Int!
product: Product!
}
type Query {
reviews: [Review!]!
}

Now, let's say a client executes the following query against the gateway:

query GetAllReviews {
reviews {
score
product {
name
}
}
}

This query's execution starts in the Reviews , but it needs to obtain each Product's name from the Products . As part of this process, the Products must resolve references to Products that are provided by the Reviews .

To help resolve these references, the Reviews must return each Product's id , even though that isn't included in the original query. This is because id is a @key for Product.

Therefore, this query contributes one field execution and zero requesting operations to Product.id. Similar logic applies to s that use the @requires directive.

Reporting field usage

Your GraphQL server can report metrics for executions, requesting s, or both.

Prerequisites

⚠️ Before continuing, make sure you've configured your router or individual GraphQL server to push metrics to GraphOS.

To report each type of usage metric to , make sure your graph meets the prerequisites for that metric:

Requesting operations

To report metrics for requesting s, your GraphQL server must run Apollo Server 3.6 or later.

If you have a federated graph, your gateway must run Apollo Server 3.6 or later, but there are no requirements for your s.

Field executions

To report metrics for executions, your GraphQL server can run any recent version of Apollo Server 2.x or 3.x.

If you have a federated graph, your s must support federated tracing. For compatible libraries, see the FEDERATED TRACING entry for libraries in this table.

If some of your s support federated tracing and others don't, only executions in compatible s are reported to Apollo.

Reporting with a supergraph

If you have a self-hosted , you only need to configure your router to send metrics to GraphOS. s should not send any metrics to directly. Instead, they can include trace data in their responses to the . The then includes that data in its own reports to .

Performance considerations

Calculating execution metrics can affect performance for large queries or high-traffic graphs. This is especially true for federated graphs, because a includes each 's full trace data in its response to the gateway.

Disabling field execution metrics

In Apollo Server 3.6 and later, you can disable -level instrumentation for some or all s by providing the fieldLevelInstrumentation option to ApolloServerPluginUsageReporting.

Disabling -level instrumentation for a particular request has the following effects:

  • The request does not contribute to the " executions" statistic on the s page in Studio.
  • The request does not contribute to -level execution timing hints that can be displayed in the and in VS Code.
  • The request does not produce a trace that can be viewed in the Traces section of the s page in Studio.

These requests do still contribute to most features of Studio, such as , the s page, and the "requesting s" statistic on the s page.

To disable -level instrumentation for all requests, pass () => false as the fieldLevelInstrumentation option:

new ApolloServer({
plugins: [
ApolloServerPluginUsageReporting({
fieldLevelInstrumentation: () => false
})
]
// ...
});

If you do this, execution statistics do not appear on the s page.

Fractional sampling

You can enable -level instrumentation for a fixed fraction of all requests by passing a number between 0 and 1 as the fieldLevelInstrumentation option:

new ApolloServer({
plugins: [
ApolloServerPluginUsageReporting({
fieldLevelInstrumentation: 0.01
})
]
// ...
});

If you do so, Apollo Server randomly chooses to enable -level instrumentation for each request according to the given probability.

⚠️ Make sure to pass a number (like 0.01), not a function that always returns the same number (like () => 0.01), which has a different effect.

In this case, whenever -level instrumentation is enabled for a particular request, Apollo Server reports it to Studio with a weight based on the given probability. The " executions" statistic on the s page (along with execution timing hints) is scaled by this weight.

For example, if you pass 0.01, your server enables -level execution for approximately 1% of requests, and every observed execution is counted as 100 executions on the s page. (The actual observed execution count is available in a tooltip in the table.)

Custom sampling

You can decide whether to enable -level instrumentation (and what the weight should be) on a per- basis by passing a function as the value of fieldLevelInstrumentation.

For example, you might want to enable -level instrumentation more often for rare s and less often for common s. For details, see the usage reporting plugin docs.

Previous
Operation metrics
Next
Segmenting by client
Edit on GitHubEditForumsDiscord