Operation signatures
Understand how GraphOS identifies equivalent operations
When you're viewing your graph's
Though each operation in the above example is named MyQuery
, GraphOS treats them as different operations because they request different fields.
To help GraphOS identify functionally identical operations, your router generates an operation signature for each operation it reports to Studio. This signature is a normalized representation for an operation with deterministic field order, whitespace, and values for in-line argument values.
Why do we need an operation signature?
Consider the following operations:
query GetPostDetails($postId: String!) {post(id: $postId) {authorcontent}}query GetPostDetails($postId: String!) {post(id: $postId) {content # Different field orderauthor}}query GetPostDetails($postId: String!) {post(id: $postId) {writer: author # Field aliascontent}}
Despite some cosmetic differences (including comments), all these operations execute identically on a particular GraphQL server. Therefore, Studio should group them when displaying performance metrics.
Apollo libraries use a
Signature algorithm
ⓘ NOTE
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
The signature algorithm performs the following modifications on an operation to generate its signature:
1. Transform in-line argument values
If an operation includes any in-line argument values, the algorithm transforms those values according to their type:
Boolean
andenum
values are preserved.Int
andFloat
values are replaced with0
.String
, list, and object values are replaced with their "empty" counterpart (""
,[]
, or{}
).
ⓘ NOTE
Argument values provided as GraphQL variable names are preserved.
2. Remove extraneous characters
The algorithm removes most unnecessary characters in the operation definition, including comments and redundant whitespace.
If the operation document includes multiple operations, then operations besides the executed operation are removed.
If the operation document includes fragments that aren't used by the executed operation, then those fragments are removed (other fragments are preserved).
3. Reorder definitions
Any preserved fragment definitions appear first, sorted alphanumerically by fragment name. The definition of the executed operation appears after all fragment definitions.
ⓘ NOTE
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 fragment's fields, field selections are sorted in the following order:
- Individually listed fields
- Named fragment spreads
- In-line fragments
Within each of these, sorting is alphanumeric by field name or fragment name.
Field aliases
All field aliases are removed. If an operation includes three instances of the same field with different aliases, then that field is listed in the signature three times with its non-aliased name.
Directives and arguments
If multiple directives are applied to the same location in the operation document, those directives are sorted alphanumerically.
If a single field accepts multiple arguments, those arguments are sorted alphanumerically by name.
Example signature
Consider this operation:
# Operation definition needs to appear after all fragment definitionsquery GetUser {user(id: "hello") {# Replace string argument value with empty string...NameParts # Spread fragment needs to appear after individual fieldstimezone # Needs to appear alphanumerically after `name`aliased: name # Need to remove alias}}# Excessive characters (including this comment!) need to be removedfragment NameParts on User {firstnamelastname}
The signature algorithm generates the following signature for this operation:
fragment NameParts on User {firstnamelastname}query GetUser {user(id: "") {nametimezone...NameParts}}
Signatures and sensitive data
The signature algorithm's primary purpose is to group operations that differ only cosmetically in terms of whitespace, field order, aliases, etc. As an additional effect, the algorithm does
However, you should not rely on this! Ideally, your sensitive data should never reach GraphOS Studio in the first place. Whenever possible,
Logging signatures at runtime
To view operation signature hashes in your own logging tools, you can create an context
. The relevant values are available in the queryHash
and operationName
properties:
const logOperationSignaturePlugin = {async requestDidStart() {return {async didResolveOperation(ctx) {console.log({queryHash: ctx.queryHash,operationName: ctx.operationName,});},};},};const server = new ApolloServer({schema,plugins: [logOperationSignaturePlugin],});
const logOperationSignaturePlugin = {async requestDidStart() {return {async didResolveOperation(ctx) {console.log({queryHash: ctx.queryHash,operationName: ctx.operationName,});},};},};const server = new ApolloServer({schema,plugins: [logOperationSignaturePlugin],});