Docs
Launch GraphOS Studio

Operation signatures

How GraphOS identifies equivalent operations


When you're viewing your graph's operation metrics, Apollo Studio groups together s based on the exact set of s they include, not based on names. This means that s with the same name but different sets of s are displayed separately:

Separate operations with same name in Studio

To help identify functionally identical s, your generates the signature for each it reports to Studio. This signature is a normalized representation for an with deterministic order, whitespace, and values for in-line values.

Why do we need an operation signature?

Consider the following s:

query GetPostDetails($postId: String!) {
post(id: $postId) {
author
content
}
}
query GetPostDetails($postId: String!) {
post(id: $postId) {
content # Different field order
author
}
}
query GetPostDetails($postId: String!) {
post(id: $postId) {
writer: author # Field alias
content
}
}

Despite some cosmetic differences (including comments), all of these s execute identically on a particular GraphQL server. Therefore, Studio should group them when displaying performance information.

Helpfully, Apollo libraries use a signature algorithm to generate the exact same operation signature for all of these s, which means Studio does group them.

Signature algorithm

⚠️ This algorithm is subject to change. This article describes the signature algorithm as of the release of Apollo Server 3.6.1. It is provided here for informational purposes, not as a canonical specification.

Feel free to jump to an example or view the source.

The signature algorithm performs the following modifications on an to generate its signature:

1. Transform in-line argument values

If an includes any in-line values, those values are transformed according to their type:

  • Boolean and enum values are preserved.
  • Int and Float values are replaced with 0.
  • String, list, and object values are replaced with their "empty" counterpart ("", [], or {}).

values provided as GraphQL names are preserved.

2. Remove extraneous characters

Most unnecessary characters in the definition (including comments and redundant whitespace) are removed.

If the document includes multiple s, then s besides the executed are removed.

If the document includes s that aren't used by the executed , then those s are removed (other s are preserved).

3. Reorder definitions

Any preserved definitions appear first, sorted alphanumerically by name. The definition of the executed appears after all definitions.

Whenever the names of two sorted items are identical, the algorithm preserves the original order of those items relative to each other.

Fields

For a given object or 's s, selections are sorted in the following order:

  1. Individually listed s
  2. Named spreads
  3. In-line s

Within each of these, sorting is alphanumeric by name or name.

Field aliases

All es are removed. If an includes three instances of the same with different es, then that is listed in the signature three times with its non-ed name.

Directives and arguments

If multiple s are applied to the same location in the document, those s are sorted alphanumerically.

If a single accepts multiple s, those s are sorted alphanumerically by name.

Example signature

Consider this :

# Operation definition needs to appear after all fragment definitions
query GetUser {
user(id: "hello") {
# Replace string argument value with empty string
...NameParts # Spread fragment needs to appear after individual fields
timezone # Needs to appear alphanumerically after `name`
aliased: name # Need to remove alias
}
}
# Excessive characters (including this comment!) need to be removed
fragment NameParts on User {
firstname
lastname
}

The signature algorithm generates the following signature for this :

fragment NameParts on User {
firstname
lastname
}
query GetUser {
user(id: "") {
name
timezone
...NameParts
}
}

Signatures and sensitive data

The signature algorithm's primary purpose is to group together s that differ only cosmetically (in terms of whitespace, order, es, and so on). As an additional effect, the algorithm does remove most in-line argument values, which in theory helps maintain data privacy.

However, you should not rely on this! Ideally, your sensitive data should never reach Apollo Studio in the first place. Whenever possible, use GraphQL variables instead of in-line values for s. This helps you control exactly which values are reported to Apollo. For details, see Apollo Studio data privacy and compliance.

Logging signatures at runtime

To view signature hashes in your own logging tools, you can create an Apollo Server plugin that accesses hash values from the shared context. The relevant values are available in the queryHash and operationName properties:

index.ts
const logOperationSignaturePlugin = {
async requestDidStart() {
return {
async didResolveOperation(ctx) {
console.log({
queryHash: ctx.queryHash,
operationName: ctx.operationName,
});
},
};
},
};
const server = new ApolloServer({
schema,
plugins: [logOperationSignaturePlugin],
});
index.js
const logOperationSignaturePlugin = {
async requestDidStart() {
return {
async didResolveOperation(ctx) {
console.log({
queryHash: ctx.queryHash,
operationName: ctx.operationName,
});
},
};
},
};
const server = new ApolloServer({
schema,
plugins: [logOperationSignaturePlugin],
});
Previous
Datadog forwarding
Next
Setup
Edit on GitHubEditForumsDiscord