
ex) of how to define a simple type called Person
type Person {
name: String!
age: Int!
}
type Post {
title: String!
author: Person!
}
type Person {
name: String!
age: Int!
posts: [Post!]!
}
ex)
{
allPersons {
name
}
}
the allPersons field in this query is called the "root field" of the query. Everything that follows the root field, is called the "payload" of the query. The only field that's specified in this query's payload is name.
This query returns a list of all persons stored in the database.
{
"allPersons": [
{ "name": "Johnny" },
{ "name": "Sarah" },
{ "name": "Alice" }
]
}
-> notice it only fetches the names of all persons, because allPersons only has one field called name. If the client also needs the person's age, we just need to adjust the query and add the age field.
{
allPersons {
name
age
}
}
{
allPersons {
name
age
posts {
title
}
}
}
{
allPersons(last: 2) {
name
}
}
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
result:
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
variables
{
"episode": "JEDI"
}
query HeroComparison($first: Int = 3) {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
friendsConnection(first: $first) {
totalCount
edges {
node {
name
}
}
}
}
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
//variable
{
"ep": "JEDI"
}
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
//result
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Han Solo"
},
{
"__typename": "Human",
"name": "Leia Organa"
},
{
"__typename": "Starship",
"name": "TIE Advanced x1"
}
]
}
}
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
//variables
{
"episode": "JEDI",
"withFriends": false
}
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
//variables
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
ex) how to create a new Person
mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}
-> notice the syntax is similar to that of queries, but mutations require the keyword "mutation".
-> createPerson is the root field of the above mutation.
-> server response for the above mutation
"createPerson": {
"name": "Bob",
"age": 36,
}
type Person {
id: ID!
name: String!
age: Int!
subscription {
newPerson {
name
age
}
}
{
"newPerson": {
"name": "Jane",
"age": 23
}
}
type Character {
name: String!
appearsIn: [Episode!]!
}
type Starship {
id: ID!
name: String!
length(unit: LenghUnit = METER): Float
}
// the field "length" has one defined argument "unit"
schema {
query: Query
mutation: Mutation
}
query {
hero {
name
}
droid(id: "2000") {
name
}
}
-- for a query like the above, it means that the GraphQL service needs to have a "Query" type with "hero" and "droid" fields.
type Query {
hero(episode: Episode): Character
droid(id: ID!): Droid
}
{
hero {
name
appearsIn
}
}
from the above query, the "name" and "appearsIn" fields will resolve to scalar types, and they are the leaves of the query (they do not have any sub-fields)
A set of default scalar types from GraphQL
1) Int: A signed 32-bit integer.
2) Float: A signed double-precision floating-point value.
3) String: A UTF-8 character sequence.
4) Boolean: true or false
5) ID: represents a unique identifier, often used to refetch an object or as the key for a cache. It behaves like String, but defining it as an ID instead of a String shows that it is not intended to be human-readable.
you can make a custom scalar type which we need to defind how that type should be serialized, deseralized and validated.
ex)
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
type Character {
name: String!
appearsIn: [Episode]!
}
query DroidById($id: ID!) {
droid(id: $id) {
name
}
}
// variable
{
"id": null
}
// will throw an error as a result
{
"errors": [
{
"message": "Variable \"$id\" of non-null type \"ID!\" must not be null.",
"locations": [
{
"line": 1,
"column": 17
}
]
}
]
}
myField: [String!]
-> this means that the list itself can be null, but it cannot have any null members.
myField: null // valid
myField: [] // valid
myField: ['a', 'b'] // valid
myField: ['a', null, 'b'] // error
myFeild: [String]!
myField: nulll // error
myField: [] // valid
myField: ['a','b'] // valid
myField: ['a', null, 'b'] // valid
an Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
ex) you could have an interface "Character" that represents any character in the Star Wars trilogy:
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
ex) of some types implementing "Character" interface
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
--> both the Human and Droid types have all the fields from the Character interface, but also has extra fields, such as "totalCredits", "starships", and "primaryFunction" that are specific to that particular type of character.
ex)
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
primaryFunction
}
}
//variable
{
"ep": "JEDI"
}
above query produces an error because the Character interface does not include primaryFunction, which is field that belongs to the type Droid only.
an inline fragment can be used to ask for a field on a specific object type.
ex)
// query
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
}
}
// variable
{
"ep": "JEDI"
}
// returns
{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
union SearchResult = Human | Droid | Starship
whereever we return a "SearchResult" type in our schema, we could access one of "Human", a "Droid", or a "Starship".
members of a union type need to be concrete object types; meaning members cannot be of interfaces or other unions.
with union types, inline fragments are REQUIRED to be able to query any fields at all.
ex)
// query
{
search(text: "an") {
__typename
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
//result
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Han Solo",
"height": 1.8
},
{
"__typename": "Human",
"name": "Leia Organa",
"height": 1.5
},
{
"__typename": "Starship",
"name": "TIE Advanced x1",
"length": 9.2
}
]
}
}
{
search(text: "an") {
__typename
... on Character {
name
}
... on Human {
height
}
... on Droid {
primaryFunction
}
... on Starship {
name
length
}
}
}
note that Starship still has a name field because Starship is NOT a Character
type Query { ... }
type Mutation { ... }
type Subscription { ... }
type Query {
allPersons: [Person!]!
}
with type Query being a root type and the allPersons being a root field.
to define arguments of the allPersons field.
type Query {
allPersons(last: Int): [Person!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
type Subscription {
newPerson: Person!
}
type Query {
allPersons(last: Int): [Person!]!
allPosts(last: Int): [Post!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
updatePerson(id: ID!, name: String!, age: String!): Person!
deletePerson(id: ID!): Person!
}
type Subscription {
newPerson: Person!
}
type Person {
id: ID!
name: String!
age: Int!
posts: [Post!]!
}
type Post {
title: String!
author: Person!
}