3 Core Concepts:
- Types: What kinds of things are returned by the API?
- Schema: How can the API be queried, and how are the types related to each other?
- Requests: What does a GraphQL API request look like?
- Resolvers: How does GraphQL get the data from the data source?
A Type is an Object with specific properties. It looks like a typescript interface:
E.G.:
Fruit {
id: int
name: string
color: Color
}
Color {
id: int
name: string
}
The Schema defines the Graph - i.e. the Types and how they are connected It also defines the shape of the Query.
It looks a bit like JSON but is not JSON. No commas after each property!
e.g.
The schema must have some root level properties. These define what queries are allowed.
Here is an example root schema, which is the special Query type:
Query {
fruits: List[Fruit]
fruit(id: int): Fruit
}
There are two kinds of queries allowed:
- A "fruits" query, which returns a list of all
Fruits
- A "fruit" query, which gets a fruit by
id
Our Schema overall looks like this:
Fruit {
id: int
name: string
color: Color
}
Color {
id: int
name: string
}
Query {
fruits: List[Fruit]
fruit(id: int): Fruit
}
An example request looks like this:
- Give me all the fruit ids and names
POST {
fruits {
id
name
}
}
- Give me a specific fruit name
POST {
fruit(id:1) {
name
}
}
- Any other request will be invalid, usually a 400 error:
POST {
hello
}
This is an error because "Query" does not have "hello".
Also an error because "Fruit" does not have "price".
POST {
fruits {
price
}
}
We have to tell GraphQL how to "resolve" each Type. Which means: how to get it from the data source.
e.g. in NodeJS
Recall the types:
Fruit {
id: int
name: string
color: Color
}
Color {
id: int
name: string
}
Query {
fruits: List[Fruit]
fruit(id: int): Fruit
}
The resolvers have the same structure:
const resolvers = {
Fruit: {
id: (fruit) => fruit.id,
name: (fruit) => fruit.name,
color: (fruit) => {
// fruit will look like { id: 1, name: 'Orange', color_id: 1 }
return sql_query('SELECT * FROM colors WHERE id=?', fruit.color_id)
}
},
Color: {
id: (color) => color.id,
name: (color) => color.name
},
Query: {
fruits: () => {
return sql_query('SELECT * FROM fruits')
},
fruit: (id) => {
const fruits = sql_query('SELECT * FROM fruits WHERE id=?', id)
if (!fruits.length) {
throw new Error('Fruit not found')
}
return fruits[0]
}
}
}
const schema = graphql.createSchema(types, resolvers)
app.post('/graphql', (req, res) => {
const result = schema.query(req.data)
res.send(result)
})
In GraphQL, you can query related objects in the same request. Compared with REST, where if you got a list of Fruits, you would have to iterate through each Fruit and make a new request to get its Color, in GraphQL you can get this with one request:
POST {
fruits {
id
name
color {
id
name
}
}
}
GraphQL will use the fruits
resolver to get the list of Fruits
from the database, and then for each Fruit
it will use
the color
resolver to get the Color
from the database.