First, the advantage over REST services
Imaging you have a web app which manages a list of people. Each of them has a few friends. That becomes a “Graph” which is a CS data-structure term for items linked together with directional or un-directional relations.
If that is for REST API design, I probably have to write quite a few ones like get-people-by-id and get-people-friends-count-by-id and get-people-friends-details-by-id
If you want to get all the data, you either have to make 3 calls to get all these info above or you combine all these 3 things at server end and return back everything no matter what the user asked for. This is what is called over fetching and under fetching
GraphQL solution looks much more elegant.For situation we just need People basic info without any details
query {
People(id: 123){
name
age
}
}
For situation we want more info
query {
People(id: 123){
name
age
friendsCount
friends{
name
age
}
}
}
Even better, we can deep dive to the friends relationship loop hole
query {
People(id: 123){
name
age
friendsCount
friends{
name
age
friends{
name
age
}
}
}
}
GraphQL basic building blocks: Type, Query, Mutation and Subscription
Type is just like Object or Noun in our human languages. In the example above, People is just a type, it should look like this: (! means it’s required)
type People {
id: ID!
name: String!
age: Int
friendsCount: Int
friends: [People]
}
To describe a list, use the [] syntax just like javascript. Here are more details
Query examples are shown in the paragraph above, it’s just like a type of query language using description way.
Mutation is to update/change/remove/add data based on the types
mutation {
addPeople(name: "John Doe" age:21) {
id
age
name
}
}
Subscriptions are like the pub/sub way in messaging system. In most popular GraphQL server implementations, they are implemented using WebSocket and some solutions that support pub/sub like Redis etcWe can build a chatting app using GraphQL Subscriptions without knowing how it’s implemented under the hood using Websockets, very neat.
The following key points are very easy to overlook but very important
Type, Query, Mutation and Subscription are defined in the Schema which is like your database’s create schema scripts.All of them starts with word `Type`
type Subscription { messageAdded(channelId: ID!): Message } type query{ allPeople: [People] } type mutation{ addPeople(name:String! age:Int): People }
2. In your app client or the client tools like GraphiQL or GraphQL Playground, use commands like query, mutation and subscription.
In short, define them using type query type mutation and type subscription first then use them in client
Don’t get confused by their client usage syntax and their schema definition syntax as I did before :)
subscription {
messageAdded(channelId: 1) {
id
text
}
}
3. Query, Mutation and Subscription all can take parameters.It looks like this
query getPhotos($filter:PhotoFilter $page:DataPage $sort:DataSort)
{
allPhotos(filter:$filter paging:$page sorting:$sort){ ... }
}
Wearing double socks
When I started, I though those addPeople(name:String age:Int) things are parameters, why do we need those $xxx things.Isn’t it just like a man wearing double socks?
Actually those $.. things like the getPhotos commands above are for Client and the allPhotos ones are for Server.This does help me to understand the syntax and hope it makes sense to you too.allPhotos(…) will be sent to the server so it can resolve the answer and send back the result.On the Client side (either App client code or GraphiQL tool), you can provide the parameter(input variables) like this below
Notice you can provide the variable in the left-bottom corner of the tool for the $input variable in the mutation statement above.That UserInput! is a Input Type, just another type which is for input variables definitions
4. All the parameter and type elements like the id, username, email, phone listed above are separated by space or new-line but not commas.
Pay attention to the query/mutation statement below, it’s a space, not comma
allPhotos(filter:$filter paging:$page sorting:$sort)
This is very easy to overlook especially the variables part showing above uses commas to separate individual variables
5. Listing all the entries for a type like the id, username, email, phone above can be tedious especially if you have to specify them again and again. So there comes a handy thing called Fragment
fragment userDetails on User {
id
username
email
phone
...
...
}
Then whenever we need to get User, we can use the fragment
getUser(id: 123){
...userDetails
}
It’s pretty much like the Javascript ES6 … operator
6. Other interesting types.
Union Type is just a combination of different typesIf we have 2 types like StudyGroup and Workout, we can union them
union AgendaItem = StudyGroup | Workout
Then when we query this new type, we can use the special syntax to dynamically get related attributes.
query schedule {
agenda {
…on Workout {
name
reps
}
…on StudyGroup {
name
subject
students
}
}
}
Interface. it’s like fragment but fragment is for query and mutation, Interface is for type definitions. It just takes the common fields from different types so all those types can share common attributes
interface AgendaItem {
name: String!
start: DateTime!
end: DateTime!
}
type StudyGroup implements AgendaItem {
name: String!
start: DateTime!
end: DateTime!
participants: [User!]!
topic: String!
}
type Workout implements AgendaItem {
name: String!
start: DateTime!
end: DateTime!
reps: Int!
}
GraphQL has a lot of terms and concepts, so here comes my quick reference which I wish I knew them when I started.
For GraphQL server to work, we need to provide Schema and Resolvers
Schema is just all the Type, Query, Mutation and Subscription we talked above. Advanced types are like Enum, Scalar, List, Union, Fragment and Interface
Resolver is the implementation functions to retrieve data for the queries and mutations
For the client side, use query, mutation and subscriptionFor the server side schema definition, use type query type mutation and type subscription
These can also take Variables as input, so you can group variables together to create InputType which is just variable collection definitionsRefer to the GraphiQL screenshot above, that UserInput! is a custom Input Type
The following is an example to use App client code to do similar mutation call like the GraphiQL example
Notice the mutation statement and variables are sent separately just like the GraphiQL sample above.
import { request } from 'graphql-request'
var url = 'http://localhost:4000/graphql'
var mutation = `
mutation populate($count: Int!) {
addFakeUsers(count:$count) {
id
name
}
}
`
var variables = { count: 3 }
request(url, mutation, variables)
.then(console.log)
.catch(console.error)
It took me quite some time to get over that many terms and understand the nuance between the server definition and the client request syntax.
Hope you find all these helpful and use it to refresh the basic knowledge over time. Cheers and claps :)