Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions gaps/GAP-7/DRAFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Fully Qualified Operation Name (FQON)

**Introduction**

:: This document specifies _Fully Qualified Operation Name_ (FQON), a
human‑readable, unambiguous identifier for GraphQL operations.

The primary motivation is to define a "lookup key" that may be used in static
configuration files (e.g. for alerting rules per operation) which correctly
targets operations, but without having to update the configuration each time a
_document id_ changes.

This specification assumes usage of
[trusted documents](https://graphql.org/learn/security/#trusted-documents) and
[Git version control](https://git-scm.com/).

**Example**

Given the following operation defined in a Git repository named `yelp/frontend`,
and in a package named `styleguide`:

```graphql example
query GetHeaderData {
...
}
```

The _FQON_ for this operation may be defined as:

```example
GetHeaderData:styleguide:yelp/frontend:1
```

When defining alerting rules for this operation, we can target all versions of
this operation by omitting the _version_ suffix:

```example
GetHeaderData:styleguide:yelp/frontend
```

**Motivation**

We may identify operations using either:

- The _operation name_ (e.g. {"GetHeaderData"})
- The _document id_ produced by hashing the operation body (e.g. {"605fad0ee0a88..."})

Because GraphQL operation names are not be guaranteed to be globally unique,
they cannot reliably identify an operation across multiple platforms or
deployment versions. On the other hand, a _document id_ is guaranteed to be
unique but is inconvenient for humans to read and maintain.

Note: See <README.md> for additional context.

**Use Cases**

- A _FQON_ may be printed instead of the operation name in application logs.
This encourages correct behavior when humans use this identifier to look up
the operation body in the document registry or codebase.
- A partial _FQON_ may be used instead of a _document id_ as lookup keys in
static configuration files (e.g. alerting rules) in order to avoid duplication
and extra steps when an operation is updated (and thus, its _document id_).

## Definition

FullyQualifiedOperationName :: OperationName : Project? : RepoFullName : Version
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having Project before RepoFullName breaks my mental model a little bit. I understand Project is for monorepos that may have several clients? Then I would prefer something more like so:

GetFoo:bazcorp/qux/barpkg:1

So effectively, more like

FullyQualifiedOperationName :: OperationName : RepoOwner / RepoName [/ Subsystem]?  : Version

This way it goes from general to specific.

Actually, thinking about this, why not even put the operation name at the end?

bazcorp/qux/barpkg:GetFoo:1

For someone using Maven Coordinates a lot, this would look a lot more familiar to me

Copy link
Copy Markdown
Contributor Author

@magicmark magicmark Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ yeah this ordering is a little weird, and an artifact of how we deployed this internally.

I wanted in theory for it to be ordered like a uri "in order of decreasing significance from left to right"

So that would imply

Repo : Project : OperationName : Version

Which is the same ordering as your bazcorp/qux/barpkg:GetFoo:1.

...however, when doing this internally, I wanted operation name to appear first, so I just yanked it 🤷.

Motivation is that when displayed in logs (grafana or splunk or whatever) or dashboard panels, a long FQON is likely to get truncated in the UI. By putting the op name first, there's more a guarantee you'll still see a difference in the value between rows for different operations. So I think there's some value and practicality here - but yes it does feel kinda weird as a pure uri.

Is that tradeoff worth it? Vote now!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a similar discussion in the graphq-over-http working group.

For URLs, the last component of the path is highlighted/promoted by a lot of tooling like chrome devtools, etc...

Whether or not that applies here is an open question since those are not really part of a url. I'm not too familiar with grafana or splunk or whatever but aren't there any display options for custom fields?

Copy link
Copy Markdown
Contributor Author

@magicmark magicmark Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know which other dev tools truncate the head and not the tail? is chrome an outlier or is the more popular way round? (thinking of tools like charles/proxyman/postman/wireshark etc etc that maybe mobile folks use more)

I've gone back and forth on this a lot trying to justify why OperationName should be first, not last, but I don't have a super compelling reason beyond "it's what we already do (bunch of tooling built around this), folks are already used to it and I sill think it looks better", which is admittedly lazy :P

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I havent't done the research sadly, but will try to remember to post here when I find those!

Copy link
Copy Markdown
Contributor Author

@magicmark magicmark May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's not a clear slam dunk either way, I'm going to follow my gut on this one and merge as is. Unless you feel strongly enough otherwise.

I'm also open to switching it around in a future spec version if enough people feel strongly.


OperationName
: The _Name_ of the operation as declared on the _OperationDefinition_ node.

Project
: The identifier for the package or directory containing the operation.
: This is defined only for operations that live in a monorepo.

RepoFullName
: The full name of the Git repository (in the format of {"owner/repo"}).
Comment thread
magicmark marked this conversation as resolved.

Version
: A positive integer that increments each time the document body changes.
Comment thread
magicmark marked this conversation as resolved.
: The tuple of _OperationName_, _Project_ and _RepoFullName_ identifies a
document; the _Version_ part distinguishes its revisions.
: The _trusted document_ registry or other persistence layer must calculate or
store version numbers, starting at {1} and increasing monotonically.

**Examples**

A _Fully Qualified Operation Name_ with all parts:

```example
GetHeaderData:styleguide:yelp/frontend:1
```

A _Fully Qualified Operation Name_ which omits `Project` (i.e.
`petstore/website` is _not_ a monorepo, and has no concept of "projects"):

```example
AllPets::petstore/website:1
```

## Partial Matches

Any _FQON_ part may be omitted to perform a partial match.

With the exception of omitting _Project_, partial matches must also omit the
`:Version` suffix.

The primary use case of this specification is to omit `:Version` for use as
lookup keys in static configuration files, as a way to target to all versions
of an operation.

**Examples**

| Pattern | Matches |
| --------------------------- | ------------------------------------------------------------------------------------------------------- |
| `GetFoo::` | All operations named {"GetFoo"} |
| `::bazcorp/qux` | All operations inside the {"bazcorp/qux"} Git repository |
| `GetFoo:barpkg:bazcorp/qux` | All operations named {"GetFoo"} inside the {"barpkg"} package inside the {"bazcorp/qux"} Git repository |

## Security Considerations

It is recommended to avoid exposing FQONs in client code to avoid leaking
potentially sensitive internal repository names or project/directory names.

Clients should still send only the _document id_ over the wire, which is opaque.
96 changes: 96 additions & 0 deletions gaps/GAP-7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# GAP-7: Fully Qualified Operation Name (FQON)

Fully Qualified Operation Names (FQONs) are an alternate way to identify and
refer to GraphQL operations that provide both human readability and uniqueness
guarantees.

The primary motivation is to define a "lookup key" that may be used in static
configuration files (e.g. for alerting rules per operation) which correctly
targets operations, but without having to update the configuration each time a
_document id_ changes.

## The problem

When using
[trusted documents](https://graphql.org/learn/security/#trusted-documents), we
may identify operations in two different ways:

1. The "operation name" (e.g. `GetConsumerHeaderData`)
2. The "document id" (e.g. `605fad0ee0a88...`)

_Operation names are not guaranteed to be unique_.

When using operation names, a developer might see `"GetUserInfo"` in service log,
copy and paste that string, use `git grep` and find it in the codebase -- only
to later discover it's the _wrong_ `GetUserInfo` query!

- Query names are only guaranteed to be unique with additional tooling.
- Even if tooling guarantees uniqueness within a repository, query names could
be reused in a _different_ repository — e.g. Web/iOS/Android code might
feasibly live in different Git repositories, and reuse operation names for
similar features.
- Additionally, servers may receive traffic for different versions of
the same query in order to support old mobile application installs - which
would share the same operation name.

Document IDs are unambiguous and may be used to solve this problem. However,
document IDs are not human friendly since they don't carry any semantic meaning,
which encourages developers to use document names in instead - which leads the
footgun outlined above.

Additionally, by definition, document IDs change each time the operation is
updated. If we use document IDs as lookup keys in an alerting configuration
file, this must be updated each time a new version of the query is published.
This creates duplication and a maintenance burden.

## Solution

A _Fully Qualified Operation Name_ (FQON) is a human readable string that can
provide the same guarantees of uniqueness as document IDs, but and can be used
partially to omit the version number in such cases as alert config files.

**Example**:

```yaml
# alert_thresholds.yaml
operations:
- fqon: GetFoo::bazcorp/qux
error_ratio: 0.3
owner: myteam@example.com
- fqon: BarStuff::bazcorp/qux
error_ratio: 0.4
alert: myteam@example.com
```

## Definition

The format is:

```
name:project:repo:version
```

This is enough information to unambiguously identify the exact document
(assuming that client tooling guarantees no duplicate operation names per
project).

**Example**:

```
GetConsumerHeaderData:yelp-styleguide:yelp/frontend:3
```

The right hand side components of the FQON can be omitted for a partial match:

**Example**:

```
GetConsumerHeaderData:yelp-styleguide:yelp/frontend
```

^ this will match any version of the "same" query, and is recommended for use
as lookup keys in static configuration files

### ARN Syntax

`:` as a separator is inspired by [ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html)
13 changes: 13 additions & 0 deletions gaps/GAP-7/metadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
id: 7
title: "Fully Qualified Operation Names"
summary: >
A human-readable, unambiguous identifier for GraphQL operations. May be used
as lookup keys in static configuration files as an alternative to persisted
document IDs.
status: proposal
authors:
- name: "Mark Larah"
email: "markl@yelp.com"
githubUsername: "@magicmark"
sponsor: "@magicmark"
discussion: "https://github.com/graphql/gaps/pull/7"
Loading