GraphQL Basics

Grigor Khachatryan
devgorilla
Published in
8 min readJan 29, 2023

GraphQL is a query language for your API that was developed and open-sourced by Facebook in 2015. More about the “What is GraphQL”:

The core building block of a GraphQL schema is the “type system”. A type system defines the types of data that can be queried and the fields that can be requested for each type. The GraphQL type system includes built-in scalar types (e.g. Int, String, Boolean), as well as custom types that can be defined by the developer.

The type system consists of three main types: scalars, objects, and enums.

Scalars are the most basic type in GraphQL, and include built-in types such as Int, Float, String, Boolean, and ID. These types represent the most basic pieces of data that can be queried or manipulated in a GraphQL API.

Object types are used to represent complex data structures, and are composed of fields that are either scalars or other object types. For example, a Book object type might have fields for the title, author, and reviews.

Enums are used to represent a fixed set of possible values, such as a BookStatus enumeration with values of "AVAILABLE", "CHECKED_OUT", and "ON_HOLD".

In addition to these types, GraphQL also has several type modifiers that can be used to further describe the data:

  • Non-Null: ensures that a field cannot be null.
  • List: allows for a field to contain a list of items, such as an array of books.
  • Input: used to define input types for mutations.

GraphQL also allows for the creation of custom scalar types to represent specific types of data that are not covered by the built-in scalars.

The GraphQL type system is also used to define relationships between different types of data. This can be achieved through the use of interfaces, unions, and fields.

Interfaces define a common set of fields that multiple types must implement. For example, a Node interface might have an id field that all types that implement it must also have.

Unions allow for multiple types to be returned from a single field. For example, a search field that returns either a Book or an Author type.

Finally, fields in GraphQL can also be used to describe relationships between types. For example, the author field in the Book type would be used to describe the relationship between a Book and its Author.

Schemas

A GraphQL schema is a blueprint for your API. It defines the types of data that can be queried, the fields that can be requested for each type, and the relationships between types. A GraphQL schema is defined using the GraphQL schema language, which is a simple language for describing the structure of a GraphQL API.

Here is an example of a simple GraphQL schema that defines a `Book` type with `title` and `author` fields:

type Book {
title: String
author: String
}

This schema defines a single type, “Book”, with two fields, “title” and “author”, both of which are of type “String”.

Built-in Scalar types

GraphQL has several built-in scalar types: Int, Float, String, Boolean, and ID. These types are used to define the fields of your types and can be used in the arguments of your queries and mutations.

Here is an example of a query that requests the “title” and “author” fields of a “Book” type:

query {
book(id: 1) {
title
author
}
}

In this example, the “book” field is of type “Book” and accepts an “id” argument, which is of type “ID”. The fields “title” and “author” are both of type “String”.

Type definitions

In GraphQL, you can define your own types using the “type” keyword. A type definition defines a new type and its fields.

Here is an example of a type definition for a “Person” type with “name” and “age” fields:

type Person {
name: String
age: Int
}

Type modifiers

GraphQL allows you to modify the type of a field with the following modifiers:

  • ! denotes a non-nullable field. This means that the field must always return a value.
  • [] denotes a list of the type. This means that the field will return an array of the specified type.

Here is an example of a type definition that includes type modifiers:

type Query {
books: [Book!]!
person(id: ID!): Person!
}

In this example, the “books” field is a non-nullable list of non-nullable “Book” types, and the “person” field is a non-nullable “Person” type that takes a non-nullable “id” argument of type “ID”.

Input arguments

In GraphQL, you can pass arguments to your fields to specify additional information about the data you are requesting. These are called input arguments. Input arguments are defined using the “input” keyword.

Here is an example of a query that uses an input argument to filter a list of books by their author:

query {
books(filter: { author: "George Orwell" }) {
title
}
}

In this example, the “books” field accepts an input argument called “filter” of type “BookFilterInput”. The “BookFilterInput” type is defined as:

input BookFilterInput {
author: String
}

Input types

Input types are similar to regular types, but they are used specifically for input arguments. They are defined using the “input” keyword.

Here is an example of an input type for a “CreateBook” mutation:

input CreateBookInput {
title: String!
author: String!
}

In this example, the “CreateBookInput” type has two fields, “title” and “author”, both of which are non-nullable strings.

Custom Scalars

In addition to the built-in scalar types, GraphQL allows you to define your own custom scalar types. Custom scalar types can be used to represent more specific types of data, such as a date or a URL.

Here is an example of a custom scalar type called “Date”:

scalar Date

To use this custom scalar type, you need to specify a custom scalar resolver. Scalar resolvers are functions that define how to parse and serialize custom scalars.

Interfaces

Interfaces in GraphQL allow you to define a set of fields that multiple types can implement. This allows you to specify that certain types have a certain set of fields without having to repeat the fields on each type.

Here is an example of an interface called “Node” that defines a “id” field:

interface Node {
id: ID!
}

You can then have multiple types implement this interface:

type Book implements Node {
id: ID!
title: String
author: String
}

type Person implements Node {
id: ID!
name: String
age: Int
}

In this example, both “Book” and “Person” types implement the “Node” interface and have a non-nullable “id” field of type “ID”.

Unions

Unions in GraphQL allow you to represent multiple types as a single type. This allows you to return multiple types in the same field.

Here is an example of a union type called “SearchResult” that can represent either a “Book” or a “Person”:

union SearchResult = Book | Person

You can then use this union type in a query:

query {
search(query: "George Orwell") {
... on Book {
title
}
... on Person {
name
}
}
}

In this example, the “search” field returns a “SearchResult” union type that can be either a “Book” or a “Person” type. To access the fields of the specific type, the query uses the “…” syntax to specify the type.

Enums

Enums in GraphQL allow you to define a set of predefined values for a field. This allows you to specify that a field can only have certain values.

Here is an example of an enum type called “BookGenre”:

enum BookGenre {
SCI_FI
FANTASY
ROMANCE
MYSTERY
}

You can then use this enum type in a type definition:

type Book {
title: String
author: String
genre: BookGenre
}

In this example, the “genre” field of the “Book” type can only have the predefined values of “SCI_FI”, “FANTASY”, “ROMANCE”, and “MYSTERY”.

Queries

Queries are used to retrieve data from a GraphQL server. They are similar to GET requests in REST APIs. A query consists of a set of fields that you want to retrieve, along with any arguments and nested fields.

Here is an example of a query that retrieves the title and author of a book:

query {
book(id: 1) {
title
author
}
}

This query asks for the “book” field with an “id” argument of 1, and then requests the “title” and “author” fields of the returned “book” object.

The power of GraphQL lies in its ability to retrieve multiple resources in a single query. For example, you can retrieve information about a book and its author in a single request:

query {
book(id: 1) {
title
author {
name
bio
}
}
}

This query retrieves the title of the book, along with the name and bio of the book’s author. This is a powerful feature that can reduce the number of requests needed to retrieve the data needed for a specific use case.

Mutations

Mutations are used to create, update, or delete data on a GraphQL server. They are similar to POST, PUT, and DELETE requests in REST APIs. A mutation consists of a set of fields that you want to modify, along with any arguments and nested fields.

Here is an example of a mutation that creates a new book:

mutation {
createBook(input: {
title: "The Hobbit"
author: "J.R.R. Tolkien"
genre: FANTASY
}) {
title
author
genre
}
}

This mutation calls the “createBook” field with an “input” argument that contains the fields for the new book. The mutation then returns the title, author, and genre of the newly created book.

Subscriptions

Subscriptions are used to receive real-time updates from a GraphQL server. They are similar to WebSocket connections in REST APIs. A subscription consists of a set of fields that you want to subscribe to, along with any arguments and nested fields.

Here is an example of a subscription that listens for updates on a book:

subscription {
bookUpdates(id: 1) {
title
author
genre
}
}

This subscription calls the “bookUpdates” field with an “id” argument of 1, and then requests the title, author, and genre fields of the returned book object. The server will then push any updates on the book with the specified id to the client.

Short Comparison with REST API

REST APIs are based on the HTTP protocol, and use different endpoints for different resources. A client must send multiple requests to different endpoints to retrieve all the data needed for a specific use case. This can lead to over-fetching or under-fetching of data and can make the client code more complex.

On the other hand, GraphQL allows you to retrieve multiple resources in a single request, with the ability to specify exactly what data is needed. This can reduce the number of requests needed to retrieve the data needed for a specific use case, leading to more efficient and user-friendly data-driven applications.

In REST API, for example, if you want to retrieve the details of a book, its author and its reviews, you would have to make multiple requests to different endpoints like:

GET /books/1
GET /authors/2
GET /reviews?bookId=1

In contrast, in GraphQL, you can retrieve all the same information in a single request:

query {
book(id: 1) {
title
author {
name
bio
}
reviews {
text
rating
}
}
}

This not only reduces the number of requests needed but also makes the client code simpler as all the data needed is available in a single response.

Another advantage of GraphQL over REST is that it allows for more flexibility in the shape of the returned data. In REST, the shape of the returned data is determined by the endpoint being called, whereas in GraphQL, the shape of the returned data is determined by the client’s request. This allows the client to retrieve only the data it needs, and not have to filter through unnecessary data.

In summary, GraphQL provides a more efficient and flexible way of retrieving data, allowing for more user-friendly data-driven applications. It allows the client to specify exactly what data is needed, reducing the number of requests needed and making the client code simpler.

--

--