GAP-13: Operation Expressions
Version: Draft
Authors: Benjie Gillam
Discussion: github.com/graphql/gaps/pull/13

Operation Expressions

1Aim

A standard syntax that people, tools and documentation can use to concisely and consistently describe, reference and generate semantic positions in a GraphQL executable document.

“Schema coordinates” give a standard human- and machine-readable way to unambiguously refer to entities within a GraphQL schema: types, fields, field arguments, enum values, directives and directive arguments.

“Operation expressions” build on the schema coordinates syntax, reusing and expanding it to handle operation concerns.

2Use cases

2.1Referencing a position within a GraphQL Operation Document

Imagine you have the following GraphQL query:

{
  businesses: searchBusinesses(name: "Automotive") {
    id
    name
    owner: personByOwnerId {
      id
      name
      email # <<< HERE
    }
  }
}

You might reference the marked (<<< HERE) field with an expression such as:

  • Person.email - this is the “schema coordinate” which uniquely identifies the field, but lacks context on how we retrieved it
  • >businesses>owner>email - given the GraphQL query document, this is sufficient to uniquely identify this specific reference (caveat: duplicate fields would all be referenced with the same expression)
  • >businesses:searchBusinesses>owner:personByOwnerId>email - this contains more context than the above, indicating not just the aliases but the actual field names too; with this access to the operation document is not required to determine what was requested
  • >businesses:searchBusinesses(name:)>owner:personByOwnerId>email - this contains even more context (the argument names that were used)

These are all valid operation expressions, but they each convey different levels of context.

2.2Generating a GraphQL Operation Document quickly (Emmet-style)

> Emmet is a plugin for many popular text editors which greatly improves HTML & > CSS workflow:

Emmet is a popular syntax for quickly generating HTML/CSS. It’s easy to imagine how a operation expression syntax could be combined with a GraphQL schema definition to quickly generate GraphQL queries, mutations and subscriptions with a concise syntax. For example the expression:

>businesses:searchBusinesses(name:)>owner:personByOwnerId>email

might expand to:

query($name: String!) {
  businesses: searchBusinesses(name: $name) {
    owner: personByOwnerId {
      email
    }
  }
}

MyFragment:User.businesses>owner>email

might expand to:

fragment MyFragment on User {
  businesses {
    owner {
      email
    }
  }
}

2.4Linking from a field description to an operation path

If, for example, you were to deprecate a root-level field in your schema, you might want to indicate where the user can retrieve the equivalent data now. You could do this by including an operation expression as part of the deprecation reason:

> The Query.branchesFromFork field is being removed; please use the following > path instead: Query>repositories>forks>branches

2.5Indicating how to access a particular field

When reading the documentation of a type in GraphiQL it currently does not indicate how to reach a particular field. Though there are often infinitely many paths to reach a field, often the shortest are the most valuable, so GraphiQL could indicate a few of the shorter paths using operation expression syntax:

> User.firstName can be accessed through paths such as: > > - >me>firstName > - >articles>author>firstName > - >searchMedia>Book.author>firstName > - mutation>createUser>user>firstName

2.6Analytics

When analysing how a GraphQL schema is used, it may be useful to track statistics for each type, field, argument using Schema Coordinates; but it may also be interesting to track through what paths users are finding said fields. You could use operation expression syntax to track this:

counters['MyQuery:>city(name:)>library(number:)>book(isbn:)']++

3Syntax

3.1Pathing

Following a path from one field to the next could use the > character; this is already used in Apollo’s GraphQL documentation browser and is intuitive for navigation. This leaves . available and non-ambiguous for referring to fields on a type, which is useful when disambiguating references on a union type, for instance:

>me>media>Film.duration

might model:

{
  me {
    media {
      ... on Film {
        duration
      }
    }
  }
}

3.2Operations

The expression >me>name would expand to { me { name } }.

If you want to create a mutation or subscription operation, you can prefix the path with the operation type (you can do this for queries too, but just like in operation documents, the query keyword is optional):

  • mutation>createUser>user>name expands to mutation ($input: CreateUserInput!) { createUser(input: $input) { user { name } } }
  • subscription>currentUserUpdated>name expands to subscription { currentUserUpdated { name } }
  • query>me>name expands to query { me { name } }

You may name operations by prefixing with an operation name followed by a colon; for example:

  • MyQuery:>me>name and MyQuery:query>me>name expand to query MyQuery { me { name } }.
  • MyMutation:mutation>createUser>name expands to mutation MyMutation { createUser { name } }.
  • MySubscription:subscription>userCreated>name expands to subscription MySubscription { userCreated { name } }.

3.3Fragments

Fragments start with a type name followed by a period: User.friends>name expands to ... on User { friends { name } }.

You can name fragments by prefixing with a fragment name and a colon: FriendNames:User.friends>name expands to fragment FriendNames on User { friends { name } }.

Other examples:

  • MyFragment:Node.User.fullName:name expands to fragment MyFragment on Node { ... on User { fullName: name } }
  • MyQuery:>allEntities>edges>node>MyNodeFragment:Node.MyUserFragment:User.fullName:name expands to
  query MyQuery {
    allEntities {
      edges {
        node {
          ...MyNodeFragment
        }
      }
    }
  }

  fragment MyNodeFragment on Node {
    ...MyUserFragment
  }

  fragment MyUserFragment on User {
    fullName: name
  }
  

3.4Arguments

Arguments use the same syntax as Schema Coordinates; namely parenthesis and a colon: >searchBusinesses(name:)>city.

We also allow you to reference input objects used in arguments, for example:

>searchBusinesses(where>size>greaterThan:)>city

expands to something like:

query($whereSizeGreaterThan: Int) {
  searchBusinesses(where: { size: { greaterThan: $whereSizeGreaterThan } }) {
    city
  }
}

Further we allow for multiple arguments to be specified, joined with commas:

>searchBusinesses(where>size>greaterThan:,where>size>lessThan:,where>city>equalTo:)>name

expands to something like:

query(
  $whereSizeGreaterThan: Int
  $whereSizeLessThan: Int
  $whereCityEqualTo: String
) {
  searchBusinesses(
    where: {
      size: { greaterThan: $whereSizeGreaterThan, lessThan: $whereSizeLessThan }
      city: { equalTo: $whereCityEqualTo }
    }
  ) {
    name
  }
}

> NOTE: the following number syntax probably needs more thought. Added only for > completeness.

We also allow [number] syntax to refer to a numbered entry in a list, or [] to refer to the next entry; e.g.:

>findUsers(byIds[]:,byIds[],byIds[],byIds[5])>name

expands to something like:

query($byIds0: ID, $byIds1: ID, $byIds2: ID, $byIds5: ID) {
  findUsers(byIds: [$byIds0, $byIds1, $byIds2, null, null, $byIds5]) {
    name
  }
}

4Grammar

The Lexical Tokens below plus OperationType and Alias are defined as in the GraphQL spec. Note there are no ignored characters: whitespace is not ignored.

4.1Lexical Tokens

Letter
ABCDEFGHIJKLM
NOPQRSTUVWXYZ
abcdefghijklm
nopqrstuvwxyz
Digit
0123456789

4.2Expression Syntax

OperationType
querymutationsubscription

§Index

  1. Alias
  2. Argument
  3. Arguments
  4. Comma
  5. Digit
  6. Expression
  7. FragmentExpression
  8. Index
  9. Indexes
  10. IntegerPart
  11. IntValue
  12. Letter
  13. Name
  14. NameContinue
  15. NamePath
  16. NameStart
  17. NegativeSign
  18. NonZeroDigit
  19. OperationExpression
  20. OperationType
  21. SelectionPath
  1. 1Aim
  2. 2Use cases
    1. 2.1Referencing a position within a GraphQL Operation Document
    2. 2.2Generating a GraphQL Operation Document quickly (Emmet-style)
    3. 2.3Documentation Permalinks
    4. 2.4Linking from a field description to an operation path
    5. 2.5Indicating how to access a particular field
    6. 2.6Analytics
  3. 3Syntax
    1. 3.1Pathing
    2. 3.2Operations
    3. 3.3Fragments
    4. 3.4Arguments
  4. 4Grammar
    1. 4.1Lexical Tokens
    2. 4.2Expression Syntax
  5. §Index