DEV Community

Cover image for GraphQL for beginners
Danijel Vincijanović
Danijel Vincijanović

Posted on • Edited on

GraphQL for beginners

Whoo, already five years have passed since Facebook publicly released GraphQL in 2015. It's not anymore just a new shiny thing - GraphQL ecosystem greatly matured and you should take it into consideration when choosing between different API design approaches.

If you're new to GraphQL, this article will help you understand how the client-server communication works and what are key differences between GraphQL and, most commonly used, RESTful API.

I'll show you how to make a request from the client to the server and we'll examine what is happening in the process. So, let's get started!

Schema and data types

Imagine that you're an astronaut 👨‍🚀. You want to buy a spaceship so that you can travel the universe with your friends. As an astronaut, you know about spaceship properties so you can easily define a type for it:

type Spaceship {
   model: String!
   weight: Float
   speed: Int
   turboEnabled: Boolean   
}
Enter fullscreen mode Exit fullscreen mode

For defining Spaceship object type we've used something called "GraphQL schema definition language" or shortly - GraphQL SDL.

All Spaceship fields are built-in scalar types. GraphQL has 5 built-in scalar types: Int, Float, String, Boolean and ID. We're not restricted to only scalar types, a field type can be another object type or enumeration.

Notice how we've used an exclamation mark after the type name - String!. By using an exclamation mark we're expecting from the server to return a non-null value for the field. In the case that server returns null value for that field, an execution error will be triggered.

Now that we know how to use GraphQL SDL, let's define an object type for a shop 🛒 where we can actually buy a spaceship:

type Shop {
   name: String!
   address: String!
   spaceships: [Spaceship]
}
Enter fullscreen mode Exit fullscreen mode

Every shop has a wide range of spaceships to offer - therefore, we have a field type [Spaceship] which represents a list of spaceships. Before moving further, we need to define how we can query our data. For this purpose, we should use a special Query object type:

type Query {
   spaceships: [Spaceship]
   shop(name: String!): Shop
}
Enter fullscreen mode Exit fullscreen mode

We can look at Query fields as routes in REST - they are an entry point of the API. By examining Query type we can find out which data we can get from the server. In this case, we can get a list of spaceships and/or we can get a shop by name.

Finally, our GraphQL schema looks like this:

type Spaceship {
   model: String!
   weight: Float
   speed: Int!
   turboEnabled: Boolean   
}

type Shop {
   name: String!
   address: String!
   spaceships: [Spaceship]
}

type Query {
   spaceships: [Spaceship]
   shop(name: String!): Shop
}
Enter fullscreen mode Exit fullscreen mode

Defining a schema should not be a task for Backend developers only. Frontend developers should also take a part in it because, in the end, they will consume the data from the server and use the schema as a documentation.

Query construction

This is the part where a client comes into play. We have our schema defined so we can perform queries to fetch some data. Writing a query is simple - it's basically selecting fields that you need. Let's say that you want a list of spaceships, but you only need their model and speed, nothing else. You would write a query like this:

{
    spaceships {
        model
        speed
    }
}
Enter fullscreen mode Exit fullscreen mode

After that, perform a request to the GraphQL server with the query attached as a query parameter for GET requests or in body for POST requests.

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({query: "{ spaceships { model speed } }"})
})
Enter fullscreen mode Exit fullscreen mode

If everything went well, you'll receive a response like this:

{
  "data": {
    "spaceships": [
      {
        "model": "Mercury Conqueror",
        "speed": 2000
      }, 
      ...
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Additionally, if you want to get a shop by name along with the list of spaceships, you don't have to perform another request with a different query. You can modify the previous query and add additional fields. This way, we can get everything we need in just one request.

GRAPHQL

Things in the REST API world are a little bit different, if you want to get:

  • a list of spaceships, you would probably have to do a GET request to the /spaceships route
  • a shop by name, you would have to do a GET request to the /shop/:shopName route

REST

You may notice that we had to do more requests with REST to fetch everything we need. Not only that we did more requests but we also get data that we don't necessarily need, meaning that we are over-fetching because an endpoint returns a fixed data structure. With GraphQL, you don't have to worry about under-fetching or over-fetching because you ask only for what you need 💰.

Parse, validate and execute

We're on the server side now; handling requests in REST is straightforward - every route (endpoint) is associated with a function (controller). When server receives a request it executes the function and returns result to the client. In most cases, before reaching the controller, we'll have to parse, validate and sanitize data that we've received from the client.

On the other side, GraphQL takes the query from our request and parses it to the Abstract Syntax Tree (AST). After parsing, it will take our schema and validate received query against it. We don't have to worry if client didn't send required data, provided a string instead of a number or maybe queried non-existing fields. GraphQL takes care of it and punishes the client with an error if necessary. If everything's fine, we can proceed to the execution phase.

Execution phase

GraphQL needs to know how to resolve each field for a given query. As a reminder, our Query object type provides two possible queries: spaceships and shop(name: String!).

type Query {
   spaceships: [Spaceship]
   shop(name: String!): Shop
}
Enter fullscreen mode Exit fullscreen mode

To teach GraphQL how to resolve each field we have to write a resolver function for every Query field. The resolver function likely accesses database or does whatever needed to get the data and return it back.

const resolvers = {
  Query: {
    spaceships(obj, args, context, info) {
      return db.findAllSpaceships()
    },
    shop(obj, args, context, info) {
      return db.findShopByName(args.name)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: GraphQL is language agnostic and it is supported by many different languages. We're using JavaScript here. You can check here for more details about resolver arguments.

We can write resolvers for Spaceship and Shop object fields too. For example, we can resolve the speed field and return a different value if turboEnabled is set to true:

const resolvers = {
  Query: {...},
  Spaceship: {
    speed(obj, args, context, info) {
      return obj.turboEnabled 
         ? obj.speed * 2 
         : obj.speed
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

By default, if we omit resolvers, GraphQL resolves a field by returning property of the same name. GraphQL traverses tree and resolves each node (field). Resolved values will produce a key-value map that mirrors the original query. This result is sent to the client which requested it.

Resolvers

GraphQL use cases

Great thing about GraphQL is that you can place it on top of already existing API, so you don't have to do everything from scratch.

A common use case for using GraphQL is when the client needs the data from multiple sources. With GraphQL you can aggregate the data and let the client consume it from a single point in a standardized way.

Another use case is when there are multiple different clients which are using different data. Most likely, those clients will have to do several requests just to fetch needed data and will be prone to over-fetching and under-fetching. With GraphQL you can let every client choose which data to fetch.

What's next?

We've only scratched the surface; if you want to explore further I encourage you to check following links:

Sidenote: if you're looking for a remote JS dev, feel free to ping me 🙂

Top comments (17)

Collapse
 
ericathedev profile image
Erica Tanti

Very well explained and simple to understand 🛸 Especially loved the gifs! May I ask what you used to make them?

Collapse
 
davinc profile image
Danijel Vincijanović

Thanks a lot! 🙂 I was using PowerPoint! 😄 Recorded the presentation and converted video to the gif

Collapse
 
ericathedev profile image
Erica Tanti

Interesting, thank you, hadn't thought of that! The gifs look super smooth, nice one 😁

Collapse
 
pozda profile image
Ivan Pozderac

Great intro to the topic with cool examples, I like how you covered both frontend and backend so we know what is going on. Thanks on resources for further exploration, looking forward for more awesome articles from you! =D

Collapse
 
davinc profile image
Danijel Vincijanović

Thanks mate! 🤘

Collapse
 
firungumunene profile image
Francis Irungu Munene

Say I have a MSSQL/MySql/PostgreSQL database with two tables, to keep it simple, with say 10 and 20 fields respectively, I would have to write a separate resolver for all the 30 different fields? Can you show a simple example of such a use case?

Collapse
 
davinc profile image
Danijel Vincijanović • Edited

You don't have to write resolvers for each field. By default, if you didn't specify a resolver for a field, GraphQL will return an object property with the same name.

Imagine that you have "Spaceships" table with 30 fields. This will be our "main" resolver for querying all spaceships:

Query: {
    spaceships(obj, args, context, info) {
      return db.findAllSpaceships()
    }
}
Enter fullscreen mode Exit fullscreen mode

And now, because we didn't write resolvers for Spaceship fields, GraphQL will return spaceship.model if we queried model field, spaceship.weight for weight field and same applies for every other queryable field. Hope that this makes sense 🙂

Collapse
 
fagnerbrack profile image
Fagner Brack • Edited

what are key differences between GraphQL and, most commonly used, REST API architectural style

Comparing both with their differences makes no sense, see this for the details

A REST API Architectural style is not to make a GET request on a given end-point and is not commonly used in the wild except for the "text/html" media type.

Collapse
 
adrianbdesigns profile image
Adrian Bece

Very detailed post. Great work!

Tip: include a #beginner and/or #tutorial tags

Collapse
 
sunmik profile image
Sunmi

Informative article ! I just heard GraphQL from server engineer.

Collapse
 
squigglybob profile image
squigglybob

Thanks for the clear overview. I'm looking forward to getting into it with Gatsby...

Collapse
 
gsr7 profile image
gsr7

HI Danijel, Awesome explanation !! You make GraphQL easier for me...

Appreciated your effort and spending time to make this..

Collapse
 
guergana profile image
Guergana Tzatchkova

Best explanation I've seen so far. Thanks!

Collapse
 
charles1303 profile image
charles1303

Nice article. I have been using GraphQL for the above scenarios and I have not looked back since then 🙂.