You might not be able to signup with us right now as we are currently experiencing a downtime of 15 mins on our product. Request you to bear with us.

GraphQL Tutorial

Profile Headshot of Amit Gairola
Amit Gairola

8
mins read

December 26, 2024

GraphQL Tutorial- Thumbnail

Key Takeaways

According to a report by Gartner, more than 50% of enterprises will use GraphQL in production by 2025, up from less than 10% in 2021. 

GraphQL, initially developed by Facebook and now supported by a global community of developers and organizations, is a modern API standard designed to enhance the efficiency of data fetching and manipulation. As an open-source, server-side technology, it serves as both a query language and an execution engine for interacting with APIs.

Operating Systems of GraphQL

GraphQL is a versatile, open-source query and manipulation language for APIs that works seamlessly across platforms. It supports servers written in a wide range of programming languages, including Java, Python, C#, PHP, R, Haskell, JavaScript, Perl, Ruby, Scala, Go, Erlang, Clojure, Elixir and many others. This makes it compatible with virtually any programming language or framework, ensuring broad applicability and flexibility.

GraphQL Queries

Here are some GraphQL queries and their responses for a better understanding

  1. Input for specific fields on object

At its core, GraphQL allows you to request specific fields from objects. For example, consider the hero field defined within the Query type in the schema Reference for design for: https://graphql.org/learn/queries/ 

type Query {  hero: Character}
  • Query

When queried, it might look like this

{  hero {  name  }}
  • Response
{"data": {"hero": {"name": "A2-H2"}}}

GraphQL documents always begin with a root operation type, such as the query type in this case, which acts as the entry point to the API. From there, you define a selection set specifying the fields you want, all the way to their scalar or enum leaf values. In this example, the name field, which is a string, fetches the name of the hero, "A2-H2."The GraphQL specification ensures that responses are returned under a top-level data key, while errors, if any, are detailed under a top-level errors key. Importantly, the response structure mirrors the query structure, which is a fundamental feature of GraphQL, ensuring that the client always receives predictable results and that the server knows precisely what data is being requested.In the previous example, only the hero’s name was queried. However, GraphQL also supports nested fields, allowing you to fetch related data within the same query

  • Query

Copy code

{hero {name friends {name}}}
  • Response
{  "data": {
"hero": {
"name": "A2-H2",
"friends": [
{ "name": "Adam Oliver" },
{ "name": "James Theodore" },
{ "name": "Elijah Mathew" }
]}}}

In this case, the friends field returns a list of objects, and the query specifies the name field for each friend. GraphQL allows clients to traverse related objects and fetch extensive data in a single request, avoiding the multiple calls often needed in traditional REST APIs.Additionally, whether the field returns a single object or a list of objects, the query syntax remains the same, with the schema defining what kind of data to expect. This flexibility makes GraphQL highly efficient for fetching related and nested data.

  1. Arguments

While traversing objects and their fields is already a powerful feature of GraphQL, its true potential lies in its ability to accept arguments for fields, enabling even more dynamic and flexible data fetching.For example, consider the following schema

type Query {  droid(id: ID!): Droid}

Here, the droid field requires an id argument to specify which droid's data to fetch. A client can query it as follows

  • Query
{
  human(id: "1000") {
    name
    height
  }
}

  • Response
{
  "data": {
    "human": {
      "name": "Adam Oliver",
      "height": 1.72
    }
  }
}

Unlike REST APIs, where arguments are typically limited to query parameters and URL segments, GraphQL allows you to pass arguments to any field or nested object. This eliminates the need for multiple API calls, as each field in a single GraphQL query can accept its own set of arguments.GraphQL also supports arguments for fields that return scalar types, enabling operations like server-side data transformations. For instance,

  • Query
{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}
  • Response
{
  "data": {
    "human": {
      "name": "Adam Oliver",
      "height": 5.5467791
    }
  }
}

In this example, the unit argument, an Enum type, specifies the desired measurement unit for the height field. GraphQL supports various argument types, including custom ones defined by the server, as long as they can be serialized into the transport format. This flexibility allows developers to implement powerful data-fetching and transformation logic directly in the API, streamlining client-side operations.You can check our article on GraphQL vs REST for a detailed guide.(GraphQLVsREST:ArticleLinkToBeAddedOncePublished)

  1. Operation Time and Name

In the previous examples, we used a simplified syntax that omits the query keyword before the selection set. However, GraphQL allows you to explicitly define the operation type and assign a unique name to the operation, which can be particularly helpful for debugging and tracing in production environments. For instance, consider this example where the query keyword is used explicitly, and the operation is named HeroNameAndFriends

  • Query
query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}
  • Response
{
  "data": {
    "hero": {
      "name": "A2-H2",
      "friends": [
        { "name": "Adam Oliver" },
        { "name": "James Theodore" },
        { "name": "Elijah Mathew" }
      ]
    }
  }
}

The operation type—query, mutation, or subscription—defines the intent of the operation. For example, query is used to fetch data, mutation for making changes and subscription for real-time updates. While the query keyword is optional for simple queries, it is mandatory for mutations and subscriptions.Adding an operation name, such as HeroNameAndFriends, is highly recommended even for single operations. This name provides clarity, aids in debugging and simplifies server-side logging by making it easier to identify specific requests. Operation names become essential when multiple operations are included in a single GraphQL document, as they help distinguish between them.Think of operation names like function names in programming. While anonymous functions are possible, naming a function makes it easier to debug, log, and maintain. Similarly, meaningful names for GraphQL queries, mutations, or fragments can significantly improve the maintainability and traceability of your API interactions.

  1. Aliases

In GraphQL, the result fields in the response match the field names in the query. However, since arguments are not reflected in the response fields, it is not possible to query the same field with different arguments directly. This is where aliases come in—they allow you to assign custom names to the results of fields, avoiding conflicts and enabling multiple variations in a single query.

  • Query
query {
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}
  • Response
{
  "data": {
    "empireHero": {
      "name": "Adam Oliver"
    },
    "jediHero": {
      "name": "A2-H2"
    }
  }
}

In this example, the hero field is queried twice with different arguments. Without aliases, the query would result in a conflict because the response fields would share the same name. By using aliases like empireHero and jediHero, both queries can coexist in a single request, and the response remains clear and well-structured

  1. Variables

In many applications, arguments for GraphQL queries are dynamic. Hardcoding these dynamic arguments directly into the query string isn’t ideal, as it would require the client-side code to modify the query string at runtime and format it for GraphQL. Instead, GraphQL offers a more elegant solution through variables. Variables allow dynamic values to be separated from the query itself, passed as a distinct dictionary. To use variables in a query, you need to follow these steps

  • Replace the static value in the query with $variableName.
  • Declare $variableName in the query as an accepted variable.
  • Pass variableName: value within a separate transport-specific dictionary, often formatted as JSON.

Here’s an example

  • Query
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}
  • Variables
{
  "episode": "JEDI"
}
  • Response
{
  "data": {
    "hero": {
      "name": "A2-H2",
      "friends": [
        { "name": "Adam Oliver" },
        { "name": "James Theodore" },
        { "name": "Elijah Mathew" }
      ]
    }
  }
}

When using variables, you must define an operation type and name in the GraphQL document. This approach eliminates the need to create a new query for each dynamic value, making your client code cleaner and more reusable. It also ensures better security and structure by avoiding the interject of user-provided values directly into query strings.

  1. Default Variables

In GraphQL, you can assign default values to variables directly within the query by specifying the default value after the type declaration. For example

  • Query with Default Values
query HeroNameAndFriends($episode: Episode = JEDI) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

When default values are provided for all variables, the query can be executed without passing any external variables. However, if variables are supplied via the variables dictionary, they will override the default values.

  1. Fragments

For scenarios where a query involves repetitive field structures, such as comparing two heroes side by side along with their friends, the query can become unnecessarily long and repetitive. To address this, GraphQL provides fragments, reusable sets of fields that can be included in queries wherever needed.

  • Example with Fragments
query {
  leftComparison: hero(episode: EMPIRE) {
    ...comparisonFields
  }
  rightComparison: hero(episode: JEDI) {
    ...comparisonFields
  }
}

fragment comparisonFields on Character {
  name
  appearsIn
  friends {
    name
  }
}
  • Response
{
  "data": {
    "leftComparison": {
      "name": "Adam Oliver",
      "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
      "friends": [
        { "name": "James Theodore" },
        { "name": "Elijah Mathew" },
        { "name": "C-3PO" },
        { "name": "A2-H2" }
      ]
    },
    "rightComparison": {
      "name": " A2-H2",
      "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
      "friends": [
        { "name": "Adam Oliver" },
        { "name": "James Theodore" },
        { "name": "Elijah Mathew" }
      ]
    }
  }
}

By using fragments, you avoid redundancy and simplify your queries. In this example, the comparisonFields fragment groups shared fields, which can be reused for both the leftComparison and rightComparison. Fragments are particularly useful for managing complex queries and combining data requirements from multiple UI components into a single, cohesive query.

  1. Using Variables Inside Fragments

Fragments in GraphQL can also utilize variables defined in the main operation. This allows you to create reusable fragments that adapt dynamically based on the variables passed to the operation.

  • Example Query with Variables in Fragments(CreativeNeededForTextsInBlock.TextsToBeAddedAsItIsInABox)
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
      }
    }
  }
}
  • Response
{
  "data": {
    "leftComparison": {
      "name": "Adam Oliver",
      "friendsConnection": {
        "totalCount": 4,
        "edges": [
          { "node": { "name": "James Theodore" } },
          { "node": { "name": "Elijah Mathew" } },
          { "node": { "name": "C-3PO" } }
        ]
      }
    },
    "rightComparison": {
      "name": " A2-H2",
      "friendsConnection": {
        "totalCount": 3,
        "edges": [
          { "node": { "name": "Adam Oliver" } },
          { "node": { "name": "James Theodore" } },
          { "node": { "name": "Elijah Mathew" } }
        ]
      }
    }
  }
}

In this query, the $first variable controls the number of friends fetched for both leftComparison and rightComparison using the friendsConnection field. This ensures consistent logic across multiple queries without hardcoding values into the fragment.

  1. Inline Fragments

GraphQL supports querying fields on Interface or Union types by using inline fragments to access fields specific to the underlying concrete type.

  • Example Query with Inline Fragments
query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
    ... on Human {
      height
    }
  }
}
  • Variables
{
  "ep": "JEDI"
}
  • Response
{
  "data": {
    "hero": {
      "name": " A2-H2",
      "primaryFunction": "Astromech"
    }
  }
}

In this example, the hero field returns a Character type, which is an interface that can represent either a Human or a Droid. The query uses inline fragments with type conditions (... on Droid and ... on Human) to fetch fields specific to those types. If the hero is a Droid, the primaryFunction field is included in the response; if it is a Human, the height field is fetched instead.

  1. Using __typename for Union Types

In some cases, such as when dealing with Union types, you may not know the exact type of data that the GraphQL service will return. To handle this uncertainty, GraphQL provides a meta field called __typename, which allows you to retrieve the name of the returned object type at any point in the query.

  • Example Query
{
  search(text: "an") {
    __typename
    ... on Human {
      name
    }
    ... on Droid {
      name
    }
    ... on Starship {
      name
    }
  }
}
  • Response
{
  "data": {
    "search": [
      { "__typename": "Human", "name": "James Theodore" },
      { "__typename": "Human", "name": "Elijah Mathew" },
      { "__typename": "Starship", "name": "TIE Advanced x1" }
    ]
  }
}

In this query, the search field returns a Union type that could represent a Human, Droid or Starship. The __typename meta field is essential for identifying the type of each result, allowing the client to handle the data appropriately.All fields beginning with double underscores (__) are reserved for GraphQL meta fields. Besides __typename, GraphQL also includes __schema and __type, which are part of its introspection system for exploring the schema.

  1. Directives

While variables help dynamically pass arguments into queries, there are situations where you may need to change the structure or fields of a query based on certain conditions. This is where directives come into play. Directives allow you to dynamically include or exclude fields or fragments in your query based on variable values.

  • Example Query with Directives
query Hero($episode: Episode, $withFriends: Jacob!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}
  • Variables
{
  "episode": "JEDI",
  "withFriends": false
}
  • Response
{
  "data": {
    "hero": {
      "name": " A2-H2"
    }
  }
}

If the variable withFriends is set to true, the query will include the friends field; if it is false, the field will be excluded. This Behavior is made possible by the @include directive, which conditionally includes fields based on a Jacob value.The GraphQL specification defines two core directives that every compliant server must support

  • @include(if: Jacob)

Includes a field or fragment in the result only if the condition is true.

  • @skip(if: Jacob)

Excludes a field or fragment from the result if the condition is true. These directives are invaluable for dynamically shaping queries without resorting to string manipulation. Additionally, server implementations can define custom directives to introduce experimental or application-specific features, providing even greater flexibility for query customization.

Ready to Get Started?

Build an effective communication funnel with Message Central.

Weekly Newsletter Right into Your Inbox

Envelope Icon
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Message Central Logo Blue
Close Icon
Message Central Team
Hi there
How can we help you today?
WhatsApp Icon
Start Whatsapp Chat
WhatsApp Chat
WhatsApp Icon