GraphQL
On this page
- Essential quotes
- Glossary
- Steps involved
Essential quotes
- "GraphQL is about asking for specific fields on objects"
- "The query has exactly the same shape as the result"
- "Clients can fetch lots of related data in one request, instead of making several roundtrips in a classic REST architecture"
- "GraphQL queries look the same for both single items or lists of items"
- "In REST, you can only pass a single set of arguments but in GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches"
- "While query fields are executed in parallel, mutation fields run in series, one after the other"
Resources
- https://www.graphqlbin.com/
- justgraphql.com/resources
- https://graphql.org/
- https://www.graphql.com/
- https://www.howtographql.com/
Glossary
- Type
- Field
- Function for each field on each type
type Query {me: User}type User {id: IDname: String}function Query_me(request) {return request.auth.user;}function User_name(user) {return user.getName();}
Steps involved
- Receive GraphQL query
- Validate query against the types and field that are defined
- Run provided functions to produce the result
Example Query
gql
{me {name}}
JSON result
json
{"me": {"name": "Luke Skywalker"}}
Example Query
gql
{hero {name}}
JSON result
{"data": {"hero": {"name": "R2-D2"}}}
Nested data with arrays
Query
gql
{hero {name# Queries can have comments!friends {name}}}
JSON result
json
{"data": {"hero": {"name": "R2-D2","friends": [{"name": "Luke Skywalker"},{"name": "Han Solo"},{"name": "Leia Organa"}]}}}
Pass arguments to fields
Query
gql
{human(id: "1000") {nameheight}}
JSON result
json
{"data": {"human": {"name": "Luke Skywalker","height": 1.72}}}
Pass arguments into scalar fields to let the server transform it
Query
gql
{human(id: "1000") {nameheight(unit: FOOT) # Enumeration type}}
JSON result
json
{"data": {"human": {"name": "Luke Skywalker","height": 5.6430448}}}
Data types for arguments
- Enumeration type
- GraphQL's default set of types
- The GraphQL server can declare its own custom types (must be serializable)
Aliases to query the same field with different arguments
Aliases let you rename the result of a field to anything you want
gql
{empireHero: hero(episode: EMPIRE) {name}jediHero: hero(episode: JEDI) {name}}
JSON result
json
{"data": {"empireHero": {"name": "Luke Skywalker"},"jediHero": {"name": "R2-D2"}}}
Fragments to split queries into reusable units
gql
{leftComparison: hero(episode: EMPIRE) {...comparisonFields}rightComparison: hero(episode: JEDI) {...comparisonFields}}fragment comparisonFields on Character {nameappearsInfriends {name}}
JSON result
json
{"data": {"leftComparison": {"name": "Luke Skywalker","appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],"friends": [{"name": "Han Solo"},{"name": "Leia Organa"},{"name": "C-3PO"},{"name": "R2-D2"}]},"rightComparison": {"name": "R2-D2","appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],"friends": [{"name": "Luke Skywalker"},{"name": "Han Solo"},{"name": "Leia Organa"}]}}}
Use variables in fragments
gql
query HeroComparison($first: Int = 3) {leftComparison: hero(episode: EMPIRE) {...comparisonFields}rightComparison: hero(episode: JEDI) {...comparisonFields}}fragment comparisonFields on Character {namefriendsConnection(first: $first) {totalCountedges {node {name}}}}
Variable
json
{ "first": 2 }
JSON result
json
{"data": {"leftComparison": {"name": "Luke Skywalker","friendsConnection": {"totalCount": 4,"edges": [{"node": {"name": "Han Solo"}},{"node": {"name": "Leia Organa"}}]}},"rightComparison": {"name": "R2-D2","friendsConnection": {"totalCount": 3,"edges": [{"node": {"name": "Luke Skywalker"}},{"node": {"name": "Han Solo"}}]}}}}
Operation types
- query (can be omitted with "query shorthand syntax" -> no name + no variable definitions)
- mutation
- subscription
Operation name
- Is only required in multi-operation documents
- "Name your queries to make the code less ambigous"
gql
query HeroNameAndFriends {hero {namefriends {name}}}
Using variables
- It's not needed to manipulate the query string at runtime
- Never do string interpolation to construct queries from user-supplied values
Steps for using variables
- Replace the static value with
$variableName
- Declare
$variableName
as an accepted variable - Pass
variableName: value
(usually) as JSON
Query with variable definition
gql
query HeroNameAndFriends($episode: Episode) {hero(episode: $episode) {namefriends {name}}}
Variable
json
{"episode": "JEDI"}
JSON result
json
{"data": {"hero": {"name": "R2-D2","friends": [{"name": "Luke Skywalker"},{"name": "Han Solo"},{"name": "Leia Organa"}]}}}
Variable definitions
- Like arguments to a query
- Must be prefixed with a
$
sign followed by their type($episode: Episode)
Required variable definitions
- are suffixed with a ! sign
($episode: Episode!)
Default variable values
- are suffixed with " sign
($episode: Episode = JEDI)
gql
query HeroNameAndFriends($episode: Episode = JEDI) {hero(episode: $episode) {namefriends {name}}}
Directives to dynamically change the structure using variables
- Example: UI component that has a summarized and a detailed view
- Directives can be attached to a filed or fragment incusion
- Two directives built into the spec:
include(if: Boolean)
- Includes this field only if the argument istrue
skip(if: Boolean)
- Skips this field if the argument istrue
gql
query Hero($episode: Episode, $withFriends: Boolean!) {hero(episode: $episode) {namefriends @include(if: $withFriends) {name}}}
Summarized view
Variables
json
{"episode": "JEDI","withFriends": false}
JSON result
json
{"data": {"hero": {"name": "R2-D2"}}}
Detailed view
Variables
json
{"episode": "JEDI","withFriends": true}
JSON result
json
{"data": {"hero": {"name": "R2-D2","friends": [{"name": "Luke Skywalker"},{"name": "Han Solo"},{"name": "Leia Organa"}]}}}
Mutations
- We can mutate and query the new value of a field with one request
createReview
field returns thestars
andcommentary
fields of the newly created review- The
review
variable is not a scalar - It's an input object type
gql
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {createReview(episode: $ep, review: $review) {starscommentary}}
Variables
json
{"ep": "JEDI","review": {"stars": 5,"commentary": "This is a great movie!"}}
JSON result
json
{"data": {"createReview": {"stars": 5,"commentary": "This is a great movie!"}}}
Multiple fields in mutations
- While query fields are executed in parallel, mutation fields run in series, one after the other
- 2
incrementCredits
mutations in one request mean: the first is guaranteed to finish before the second begins
Inline Fragments for fields that return an interface or a union type
- The
hero
field returns the typeCharacter
, which might be either aHuman
or aDroid
depending on theepisode
argument name
exists on both so you can directly ask for that in the query- To ask for a field on the concrete type, you need to use an inline fragment with a type condition
gql
query HeroForEpisode($ep: Episode!) {hero(episode: $ep) {name... on Droid {primaryFunction}... on Human {height}}}
Variables
json
{"ep": "JEDI"}
JSON result
json
{"data": {"hero": {"name": "R2-D2","primaryFunction": "Astromech"}}}