Skip to main content

GraphQL

  • created at Facebook in 2012
  • it is a declarative data fetching specification & query language
    • used to get data from server to client
  • can use many languages to build a graph server

Benefits of using GraphQL

  • it defines the shape of the desired data and calls for it once
    • this helps to avoid multiple rest calls and the performance problems of over and under fetching
  • it is backward compatible and version-free
    • means that one can add new fields to an existing GraphQL server without breaking the current clients
      • old fields can be deprecated and yet still can continue to function
  • can be used to wrap around existing API
    • so that one don't have to set up everything from scratch & use it as part of the existing setup
  • it is language agnostic
    • can implement GraphQL solutions in a range of different languages

Syntax

writing comments

# comment

Queries

Basic queries

  • graphql query
// method 1: similar to anonymous function
{
viewer {
id
}
}

// method 2: makes it easier to find but does not have a unique name
query {
viewer {
id
}
}

// method 3: Set operation names to all easy finding as names are unique
query SomeUniqueName {
viewer {
id
}
}
  • json result
{
"data": {
"viewer": {
"id": "efhyubVgvkywdUBUYGGB"
}
}
}

Variable definitions

query SomeUniqueName($myVariable: String!) {
organization(login: $myVariable) {
id
}
}
  • query variables
{
"myVariable": "facebook"
}
{
"data": {
"organization": {
"id": "MDEyOk9yZ2FuaXphdGlvbjY5NjMx"
}
}
}

Multiple fields

{
viewer {
id
bio
name
company
}
}
{
"data": {
"viewer": {
"id": "HJBHJssjkbdLJHBHJB",
"bio": "some description about myself",
"name": "Myname",
"company": null
}
}
}

Parsing argument

  • when parsing argument value, must use double quotes
  • single argument
{
repositoryOwner(login: "myusername") {
id
url
}
}
{
"data": {
"repositoryOwner": {
"id": "hlbBHKBbhbdhbBLHB",
"url": "https://github.com/myusername"
}
}
}
  • multiple arguments
{
repository(name: "graphql", owner: "facebook") {
id
}
}
{
"data": {
"repository": {
"id": "MDEwOlJlcG9zaXRvcnkzODM0MjIyMQ=="
}
}
}

Required argument

  • if missing an error will be shown
{
repository(name: "graphql") {
id
}
}
{
"errors": [
{
"path": ["query", "repository"],
"extensions": {
"code": "missingRequiredArguments",
"className": "Field",
"name": "repository",
"arguments": "owner"
},
"locations": [
{
"line": 2,
"column": 2
}
],
"message": "Field 'repository' is missing required arguments: owner"
}
]
}

Aliases

  • not using aliases with conflicting arguments will result with an error
{
repository(name: "graphql", owner: "facebook") {
id
description
homepageUrl
}
repository(name: "react", owner: "facebook") {
id
description
homepageUrl
}
}
{
"errors": [
{
"path": [],
"extensions": {
"code": "fieldConflict",
"fieldName": "repository",
"conflicts": "{name:\"\\\"graphql\\\"\",owner:\"\\\"facebook\\\"\"} or {name:\"\\\"react\\\"\",owner:\"\\\"facebook\\\"\"}"
},
"locations": [
{
"line": 2,
"column": 3
},
{
"line": 5,
"column": 3
}
],
"message": "Field 'repository' has an argument conflict: {name:\"\\\"graphql\\\"\",owner:\"\\\"facebook\\\"\"} or {name:\"\\\"react\\\"\",owner:\"\\\"facebook\\\"\"}?"
}
]
}
  • Use Aliases to solve the error
{
graphqlProject: repository(name: "graphql", owner: "facebook") {
id
description
homepageUrl
}
reactProject: repository(name: "react", owner: "facebook") {
id
description
homepageUrl
}
}
{
"data": {
"graphqlProject": {
"id": "MDEwOlJlcG9zaXRvcnkzODM0MjIyMQ==",
"description": "GraphQL is a query language and execution engine tied to any backend service.",
"homepageUrl": "https://spec.graphql.org"
},
"reactProject": {
"id": "MDEwOlJlcG9zaXRvcnkxMDI3MDI1MA==",
"description": "A declarative, efficient, and flexible JavaScript library for building user interfaces.",
"homepageUrl": "https://reactjs.org"
}
}
}

Fragments

  • they are reusable sets of fields that can be included in queries as needed to prevent repetition
{
graphqlProject: repository(name: "graphql", owner: "facebook") {
...repoFields
}
reactProject: repository(name: "react", owner: "facebook") {
...repoFields
}
}

fragment repoFields on Repository {
id
description
homepageUrl
}
{
"data": {
"graphqlProject": {
"id": "MDEwOlJlcG9zaXRvcnkzODM0MjIyMQ==",
"description": "GraphQL is a query language and execution engine tied to any backend service.",
"homepageUrl": "https://spec.graphql.org"
},
"reactProject": {
"id": "MDEwOlJlcG9zaXRvcnkxMDI3MDI1MA==",
"description": "A declarative, efficient, and flexible JavaScript library for building user interfaces.",
"homepageUrl": "https://reactjs.org"
}
}
}

Nested fields

{
viewer {
id
name
isEmployee
location
databaseId
repositories(first: 3) {
edges {
node {
id
name
}
}
}
}
}
{
"data": {
"viewer": {
"id": "MDQ6VXNlcjM1MDQyMjEz",
"name": "Terence",
"isEmployee": false,
"location": null,
"databaseId": 35042213,
"repositories": {
"edges": [
{
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMjExNDE5MzY=",
"name": "alarm-volume-control"
}
},
{
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMjgyMDY0ODM=",
"name": "Rock-Paper-Scissors"
}
},
{
"node": {
"id": "MDEwOlJlcG9zaXRvcnkxMjgzOTQzNjc=",
"name": "Hackerrank-Solutions"
}
}
]
}
}
}
}

Multiple nested fields

{
repository(owner: "github", name: "opensource.guide") {
id
name
description
watchers(first: 3) {
edges {
node {
id
name
company
}
}
}
pullRequests(last: 2) {
edges {
node {
id
author {
avatarUrl
login
resourcePath
}
}
}
}
}
}
{
"data": {
"repository": {
"id": "MDEwOlJlcG9zaXRvcnk2MTIwNDgxOA==",
"name": "opensource.guide",
"description": "📚 Community guides for open source creators",
"watchers": {
"edges": [
{
"node": {
"id": "MDQ6VXNlcjEwODcy",
"name": "TAKAGI Masahiro",
"company": null
}
},
{
"node": {
"id": "MDQ6VXNlcjMwNDA4",
"name": "Dirk Brünsicke",
"company": "bruensicke.com"
}
},
{
"node": {
"id": "MDQ6VXNlcjQwNDE1",
"name": "Mike Linksvayer",
"company": "☃"
}
}
]
},
"pullRequests": {
"edges": [
{
"node": {
"id": "MDExOlB1bGxSZXF1ZXN0Njg4NjMzNTE2",
"author": {
"avatarUrl": "https://avatars.githubusercontent.com/u/86223196?v=4",
"login": "maxius122-bit",
"resourcePath": "/maxius122-bit"
}
}
},
{
"node": {
"id": "MDExOlB1bGxSZXF1ZXN0Njg4NjM0OTQ1",
"author": {
"avatarUrl": "https://avatars.githubusercontent.com/u/86223196?v=4",
"login": "maxius122-bit",
"resourcePath": "/maxius122-bit"
}
}
}
]
}
}
}
}

Pagination

  • first, last, states can be used to filter the data
# (first: 5)
# (last: 3)

{
repository(name: "graphql", owner: "facebook") {
id
issues(last: 5, states: OPEN) {
edges {
node {
id
number
title
}
}
}
}
}
{
"data": {
"repository": {
"id": "MDEwOlJlcG9zaXRvcnkzODM0MjIyMQ==",
"issues": {
"edges": [
{
"node": {
"id": "MDU6SXNzdWU4NzMxNzU4MTI=",
"number": 862,
"title": "Meta-Fields Defined by Schema Authors"
}
},
{
"node": {
"id": "MDU6SXNzdWU4NzYwNzYyODQ=",
"number": 864,
"title": "Explicitly state in spec that full introspection output is equivalent to full SDL document"
}
},
{
"node": {
"id": "MDU6SXNzdWU4Nzc3NjEzNTQ=",
"number": 866,
"title": "Execute fragments directly"
}
},
{
"node": {
"id": "MDU6SXNzdWU4ODAyNjEzNTE=",
"number": 867,
"title": "[Strawman] Provide a non-null designator in GraphQL operations"
}
},
{
"node": {
"id": "MDU6SXNzdWU4OTg4OTY4NzQ=",
"number": 872,
"title": "Optional v. Nullable input redux"
}
}
]
}
}
}
}

Mutations

  • Data modifications are made with mutations
  • Similar to PUT or DELETE in REST
  • data is sent as a payload
  • GraphQL changes the dataset behind the schema
  • API defines which mutations are allowed

Create mutations

mutation NewComment($input: AddCommentInput!) {
addComment(input: $input) {
clientMutationId
subject {
id
}
}
}
  • query variables
{
"input": {
"clientMutationId": "123456",
"subjectId": "NININIWDNEUBNFIUuinife",
"body": "mutation has occured"
}
}
  • json result (shows that a new comment has been added to the issue of the repository)
{
"data": {
"addComment": {
"clientMutationId": "123456",
"subject": {
"id": "MDU6SXNzdWU5NDY1MjM3MTM="
}
}
}
}

Schemas

  • the way the fields are setup in graphQL is determined by the schemas
  • it provides sll the object types used in the data
    • it also specifies the types for all the values

Input Types

  • Integer
  • Float
  • String
  • Boolean
  • Null
  • Enum
  • List
  • Object

Query __schema

{
__schema {
queryType {
name
description
fields {
name
description
}
}
}
}
{
"data": {
"__schema": {
"queryType": {
"name": "Query",
"description": "The query root of GitHub's GraphQL interface.",
"fields": [
{
"name": "codeOfConduct",
"description": "Look up a code of conduct by its key"
},
{
"name": "codesOfConduct",
"description": "Look up a code of conduct by its key"
},
...,
{
"name": "viewer",
"description": "The currently authenticated user."
}
]
}
}
}
}
  • sample schema in the docs
marketplaceCategories(
excludeEmpty: Boolean
excludeSubcategories: Boolean
includeCategories: [String!] # ! means value is required
): [MarketplaceCategory!]!
Get alphabetically sorted list of Marketplace categories

marketplaceCategory(slug: String!useTopicAliases: Boolean): MarketplaceCategory
Look up a Marketplace category by its slug.

marketplaceListing(slug: String!): MarketplaceListing
Look up a single Marketplace listing

marketplaceListings(
adminId: ID
after: String
allStates: Boolean
before: String
categorySlug: String
first: Int
last: Int
organizationId: ID
primaryCategoryOnly: Boolean = false
slugs: [String]
useTopicAliases: Boolean
viewerCanAdmin: Boolean
withFreeTrialsOnly: Boolean = false
): MarketplaceListingConnection!
Look up Marketplace listings

Query __type

{
__type(name: "Repository") {
kind
name
description
}
}
{
"data": {
"__type": {
"kind": "OBJECT",
"name": "Repository",
"description": "A repository contains the content for a project."
}
}
}