Skip to content

Instantly share code, notes, and snippets.

@odigity
Last active March 13, 2022 19:42
Show Gist options
  • Save odigity/a80f73854106706785b1646948f971f0 to your computer and use it in GitHub Desktop.
Save odigity/a80f73854106706785b1646948f971f0 to your computer and use it in GitHub Desktop.
GraphQL Cheat Sheet (not yet updated for 2021)
┌────────────────────────────┐
• Overview
• Types
• Server Examples
• Client Examples
• Responses
• Errors & Non-Nullability
• Subscriptions
└────────────────────────────┘
┌──────────┐
──┤ Overview ├─────────────────────────────────────────────────────────────────
└──────────┘
GraphQL consists of a type system, query language and execution semantics, static validation, and type introspection.
Server Document (aka Schema)
• list of object type defs (inc roots -> Query/Mutation/Subscription)
• field defs
• arguments defs -aka- input value defs
Client Document (aka Query or Request)
• list of operation defs
• variable defs
• selection set -aka- fields
• arguments (literals|variables)
• desired operation name
• values for variables -> values for arguments
Fragments are the primary unit of query composition in GraphQL.
── Schema & Roots ──
# optional / implicit
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
┌───────┐
──┤ Types ├────────────────────────────────────────────────────────────────────
└───────┘
── Input Types & Output Types ──
Input Types : Scalar, Enum, InputObject # used in: variables (-> operations), arguments (-> fields/directives)
Output Types : Scalar, Enum, Object, Interface, Union
── Named Types & Wrapping types ──
Named Types : Scalar, Enum, Object, Interface, Union, InputObject
Wrapping Types : List, Not-Null
A wrapping type has an underlying named type, found by continually unwrapping the type until a named type is found.
── Built-In Scalar Types ──
Int, Float, String, Boolean, ID
Numeric integer values larger than 32‐bit should either use String or a custom‐defined Scalar type,
as not all platforms and transports support encoding integer numbers larger than 32‐bit.
The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache.
The ID type is serialized in the same way as a String; however, it is not intended to be human‐readable.
┌─────────────────┐
──┤ Server Examples ├──────────────────────────────────────────────────────────
└─────────────────┘
type Query {
hero(episode: Episode): Character
human(id: String!): Human
droid(id: String!): Droid
}
enum Episode { NEWHOPE, EMPIRE, JEDI }
interface Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
primaryFunction: String
}
union SearchResult = Photo | Person
input Point2d {
x: Float
y: Float
}
── Directives ──
directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE # reason String allows Markdown
── Descriptions ──
Description strings occur immediately before the definition they describe, and allow Markdown.
"A simple GraphQL schema which is well described."
type Query {
"Translates a string from a given language into a different language."
translate(
"The original language that `text` is provided in."
fromLanguage: Language
"The translated language to be returned."
toLanguage: Language
"The text to be translated."
text: String
): String
}
┌─────────────────┐
──┤ Client Examples ├──────────────────────────────────────────────────────────
└─────────────────┘
{ hello } # If a document contains only one query operation, and that query defines no variables and contains no directives,
# that operation may be represented in a short‐hand form which omits the query keyword and query name.
fragment shred on SomeType {
# fields
}
query [Foo] ($foo: String) {
bar # simple scalar field
myAlias: baz(id: 57) # argument w/literal
berz(name: $foo) { # argument w/variable
...shred # fragment spread
... on SomeType { # inline fragment
# fields
}
}
bangerz(rand: 5) {
... @include(if: $expandedInfo) { # inline fragment wo/type condition and w/directive
firstName
lastName
birthday
}
}
}
mutation [Foo] {
doThing(thingID: 12345) {
result
}
}
subscription...
── Directives ──
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
┌───────────┐
──┤ Responses ├────────────────────────────────────────────────────────────────
└───────────┘
{
# must not be present if operation failed before execution
# must be null if error encountered during execution that prevented a valid response
data: { ... } # object of root type (Query/Mutation)
# must not be present unless there are errors
# must be present and contain at least one error if 'data' entry not present
errors: [
{
message: "..." # required
locations: [ # optional; refers to request document
{ line: <line>, column: <column> },
....
]
path: [ "<level1>", ... ] # path of erroneous response field; ex: [ "hero", "heroFriends", 1, "name" ]
# path segments that represent fields should be strings,
# and path segments that represent list indices should be 0‐indexed integers
extensions: {} # optional
},
...
]
extensions: {} # optional
}
┌──────────────────────────┐
──┤ Errors & Non-Nullability ├─────────────────────────────────────────────────
└──────────────────────────┘
• If an error is thrown while resolving a field,
it should be treated as though the field returned null,
and an error must be added to the "errors" list in the response.
• If the result of resolving a field is null
(either because the function to resolve the field returned null or because an error occurred),
and that field is of a Non-Null type, then a field error is thrown.
The error must be added to the "errors" list in the response.
• If the field returns null because of an error which has already been added to the "errors" list in the response,
the "errors" list must not be further affected.
That is, only one error should be added to the errors list per field.
• Since Non-Null type fields cannot be null, field errors are propagated to be handled by the parent field.
If the parent field may be null then it resolves to null,
otherwise if it is a Non-Null type, the field error is further propagated to it’s parent field.
• If a List type wraps a Non-Null type, and one of the elements of that list resolves to null,
then the entire list must resolve to null.
If the List type is also wrapped in a Non-Null, the field error continues to propagate upwards.
• If all fields from the root of the request to the source of the field error return Non-Null types,
then the "data" entry in the response should be null.
┌───────────────┐
──┤ Subscriptions ├────────────────────────────────────────────────────────────
└───────────────┘
If the operation is a subscription, the result is an event stream called the “Response Stream”
where each event in the event stream is the result of executing the operation for each new event on an underlying “Source Stream”.
Executing a subscription creates a persistent function on the server that maps an underlying Source Stream to a returned Response Stream.
subscription NewMessages {
newMessage(roomId: 123) {
sender
text
}
}
While the client is subscribed, whenever new messages are posted to chat room with ID “123”,
the selection for “sender” and “text” will be evaluated and published to the client, for example:
{
"data": {
"newMessage": {
"sender": "Hagrid",
"text": "You're a wizard!"
}
}
}
Query and mutation operations are stateless, allowing scaling via cloning of GraphQL server instances.
Subscriptions, by contrast, are stateful and require maintaining the GraphQL document, variables, and other context over the lifetime of the subscription.
GraphQL subscriptions do not require any specific serialization format or transport mechanism.
Subscriptions specifies algorithms for the creation of a stream, the content of each payload on that stream, and the closing of that stream.
There are intentionally no specifications for message acknoledgement, buffering, resend requests, or any other quality of service (QoS) details.
Message serialization, transport mechanisms, and quality of service details should be chosen by the implementing service.
A Source Stream represents the sequence of events, each of which will trigger a GraphQL execution corresponding to that event.
Like field value resolution, the logic to create a Source Stream is application‐specific.
Each event in the underlying Source Stream triggers execution of the subscription selection set using that event as a root value.
┌─────────────┐ https://graphql.github.io/graphql-spec/June2018/
──┤ Spec T.O.C. ├──────────────────────────────────────────────────────────────
└─────────────┘
○ Language Source Text • Document • Operations • Selection Sets
Fields • Arguments • Field Alias • Fragments
Input Values • Variables • Type References • Directives
○ Type System Type System Extensions • Schema • Descriptions
Types • Scalars • Objects • Interfaces • Unions • Enums
Input Objects • List • Non-Null • Directives
○ Introspection Reserved Names • Documentation • Deprecation
Type Name Introspection • Schema Introspection
○ Validation Documents • Operations • Fields • Arguments
Fragments • Values • Directives • Variables
○ Execution Executing Requests • Executing Operations
Executing Selection Sets • Executing Fields
○ Response Response Format • Serialization Format
┌─────────┐
──┤ Grammar ├──────────────────────────────────────────────────────────────────
└─────────┘
─── My Notation ───
# single-line comments * -> list
"single-line strings" [] -> optional
"""multi-line strings""" a | b -> value set
Commas are optional throughout GraphQL so trailing commas are allowed and repeated commas do not represent missing values.
Name := /[_A-Za-z][_0-9A-Za-z]*/
Document := Definition*
Definition := ExecutableDefinition | TypeSystemDefinition | TypeSystemExtension
────── Query ─────── ───────────────── Schema ─────────────────
── Query ──
ExecutableDefinition := OperationDefinition | FragmentDefinition
OperationDefinition := [OperationType [Name] [VariableDefinitions] [Directives]] SelectionSet
OperationType := 'query' | 'mutation' | 'subscription'
SelectionSet := { Selection* }
Selection := Field | FragmentSpread | InlineFragment
Field := [Alias] Name [Arguments] [Directives] [SelectionSet]
Alias := Name:
Arguments := ( Argument* )
Argument := Name : Value
FragmentSpread := ...FragmentName [Directives]
InlineFragment := ... [TypeCondition] [Directives] SelectionSet
FragmentDefinition := 'fragment' FragmentName TypeCondition [Directives] SelectionSet
FragmentName := Name but not 'on'
TypeCondition := 'on' NamedType
Value := Variable | IntValue | FloatValue | StringValue | BooleanValue | NullValue | EnumValue | ListValue | ObjectValue
BooleanValue := 'true' | 'false'
NullValue := 'null'
EnumValue := Name but not 'true','false','null'
ListValue := [ [Value*] ]
ObjectValue := { [ObjectField*] }
ObjectField := Name : Value
VariableDefinitions := ( VariableDefinition* )
VariableDefinition := Variable : Type [DefaultValue]
Variable := $Name
DefaultValue := = Value
Type := NamedType | ListType | NonNullType
NamedType := Name
ListType := [ Type ]
NonNullType := NamedType ! | ListType !
Directives := Directive*
Directive := @Name [Arguments]
── Schema ──
TypeSystemDefinition := SchemaDefinition | TypeDefinition | DirectiveDefinition
TypeSystemExtension := SchemaExtension | TypeExtension
SchemaDefinition := 'schema' [Directives] { OperationTypeDefinition* }
SchemaExtension := 'extend schema' [Directives] { OperationTypeDefinition* }
'extend schema' Directives
OperationTypeDefinition := OperationType : NamedType
Description := StringValue
TypeDefinition := ScalarTypeDefinition | ObjectTypeDefinition | InterfaceTypeDefinition | UnionTypeDefinition | EnumTypeDefinition | InputObjectTypeDefinition
TypeExtension := ScalarTypeExtension | ObjectTypeExtension | InterfaceTypeExtension | UnionTypeExtension | EnumTypeExtension | InputObjectTypeExtension
ScalarTypeDefinition := [Description] 'scalar' Name [Directives]
ScalarTypeExtension := 'extend scalar' Name Directives
ObjectTypeDefinition := [Description] 'type' Name [ImplementsInterfaces] [Directives] [FieldsDefinition]
ObjectTypeExtension := 'extend type' Name [ImplementsInterfaces] [Directives] FieldsDefinition
'extend type' Name [ImplementsInterfaces] Directives
'extend type' Name ImplementsInterfaces
ImplementsInterfaces := 'implements' [&] NamedType
ImplementsInterfaces [&] NamedType
FieldsDefinition := { FieldDefinition* }
FieldDefinition := [Description] Name [ArgumentsDefinition] : Type [Directives]
ArgumentsDefinition := ( InputValueDefinition* )
InputValueDefinition := [Description] Name : Type [DefaultValue] [Directives]
InterfaceTypeDefinition := [Description] 'interface' Name [Directives] [FieldsDefinition]
InterfaceTypeExtension := 'extend interface' Name [Directives] FieldsDefinition
'extend interface' Name Directives
UnionMemberTypes := '=' ['|'] NamedType
UnionMemberTypes | NamedType
UnionTypeDefinition := [Description] 'union' Name [Directives] [UnionMemberTypes]
UnionTypeExtension := 'extend union' Name [Directives] UnionMemberTypes
'extend union' Name Directives
EnumValuesDefinition := { EnumValueDefinition* }
EnumValueDefinition := [Description] EnumValue [Directives]
EnumTypeDefinition := [Description] 'enum' Name [Directives] [EnumValuesDefinition]
EnumTypeExtension := 'extend enum' Name [Directives] EnumValuesDefinition
'extend enum' Name Directives
InputFieldsDefinition := { InputValueDefinition* }
InputObjectTypeDefinition := [Description] 'input' Name [Directives] [InputFieldsDefinition]
InputObjectTypeExtension := 'extend input' Name [Directives] InputFieldsDefinition
'extend input' Name Directives
DirectiveDefinition := [Description] 'directive @' Name [ArgumentsDefinition] 'on' DirectiveLocations
DirectiveLocations := ['|'] DirectiveLocation
DirectiveLocations | DirectiveLocation
DirectiveLocation := ExecutableDirectiveLocation | TypeSystemDirectiveLocation
ExecutableDirectiveLocation := 'QUERY' | 'MUTATION' | 'SUBSCRIPTION' | 'FIELD' | 'FRAGMENT_DEFINITION' | 'FRAGMENT_SPREAD' | 'INLINE_FRAGMENT'
TypeSystemDirectiveLocation := 'SCHEMA' | 'SCALAR' | 'OBJECT' | 'FIELD_DEFINITION' | 'ARGUMENT_DEFINITION' | 'INTERFACE' | 'UNION' |
'ENUM' | 'ENUM_VALUE' | 'INPUT_OBJECT' | 'INPUT_FIELD_DEFINITION'
┌─────────────┐
──┤ Meta-Fields ├──────────────────────────────────────────────────────────────
└─────────────┘
Implicit - do not appear in the fields list.
type Query {
__schema: __Schema!
__type(name: String!): __Type
}
type <all Objects/Interfaces/Unions> {
__typename: String!
}
┌──────────────────────┐
──┤ Introspection Schema ├─────────────────────────────────────────────────────
└──────────────────────┘
type __Schema {
types: [__Type!]!
queryType: __Type!
mutationType: __Type
subscriptionType: __Type
directives: [__Directive!]!
}
type __Type {
kind: __TypeKind!
name: String
description: String
# OBJECT and INTERFACE only
fields(includeDeprecated: Boolean = false): [__Field!]
# OBJECT only
interfaces: [__Type!]
# INTERFACE and UNION only
possibleTypes: [__Type!]
# ENUM only
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
# INPUT_OBJECT only
inputFields: [__InputValue!]
# NON_NULL and LIST only
ofType: __Type
}
type __Field {
name: String!
description: String
args: [__InputValue!]!
type: __Type!
isDeprecated: Boolean!
deprecationReason: String
}
type __InputValue {
name: String!
description: String
type: __Type!
defaultValue: String
}
type __EnumValue {
name: String!
description: String
isDeprecated: Boolean!
deprecationReason: String
}
enum __TypeKind {
SCALAR
OBJECT
INTERFACE
UNION
ENUM
INPUT_OBJECT
LIST
NON_NULL
}
type __Directive {
name: String!
description: String
locations: [__DirectiveLocation!]!
args: [__InputValue!]!
}
enum __DirectiveLocation {
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION
}
┌──────────┐
──┤ Examples ├─────────────────────────────────────────────────────────────────
└──────────┘
{ # Query
__type(name: "User") {
name
fields {
name type { name }
}
}
}
{ # Response
"__type": {
"name": "User",
"fields": [
{ "name": "id", "type": { "name": "String" } },
{ "name": "name", "type": { "name": "String" } },
{ "name": "birthday", "type": { "name": "Date" } },
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment