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.

Home
Right Chevron Icon
Blog
Right Chevron IconRight Chevron Icon
GraphQL Tutorial

GraphQL Tutorial

Profile Headshot of Amit Gairola
Amit Gairola

8
mins read

December 26, 2024

GraphQL Tutorial- Thumbnail

Key Takeways

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.

Frequently Asked Questions

How do I choose the right OTP service provider?

When selecting an OTP SMS service provider, focus on:

  • Delivery reliability and speed
  • Global coverage and local compliance
  • Multi-channel support and fallback
  • Ease of integration
  • Pricing transparency

The right provider should not just send OTPs but ensure they are delivered consistently across regions and networks.

Not all OTP SMS service providers are built the same.

Some optimize for cost, others for flexibility but very few balance delivery reliability, global coverage and ease of use. And that balance is what actually impacts whether your users receive OTPs on time.

If OTP is critical to your product, focus on:

  • reliable delivery (not just sending)
  • multi-channel fallback
  • scalability across regions

Try It for Yourself

Why is multi-channel OTP important?

Relying only on SMS can lead to failed verifications due to:

  • network issues
  • telecom filtering
  • device limitations

Multi-channel OTP systems (SMS + WhatsApp + voice) improve success rates by automatically retrying through alternative channels if one fails.

What is the best OTP SMS service provider in India?

Some of the commonly used OTP SMS service providers in India include MSG91, Exotel and 2Factor.

That said, India has additional challenges like DLT compliance and operator filtering. Platforms that handle these internally while also offering fallback options tend to provide more consistent OTP delivery.

Which is the cheapest OTP service provider?

Providers like Fast2SMS and 2Factor are often considered among the cheapest OTP service providers, especially in India.

However, lower pricing can come with trade-offs such as:

  • lower route quality
  • higher delivery delays
  • limited fallback options

For mission-critical OTP flows, reliability often matters more than just cost.

Which is the best OTP service provider in 2026?

The best OTP service provider depends on your use case.

  • For global scale and flexibility: Twilio, Infobip
  • For cost-effective APIs: Plivo
  • For India-focused SMS OTP: MSG91, Exotel

However, platforms like Message Central stand out by balancing global coverage, multi-channel fallback and ease of deployment, making them suitable for businesses that prioritize delivery reliability.

What is an OTP service provider?

An OTP service provider enables businesses to send temporary verification codes to users via channels like SMS, WhatsApp or voice to authenticate logins, transactions or sign-ups.

Modern OTP SMS service providers go beyond just sending messages, they ensure reliable delivery using optimized routing, retries and sometimes multi-channel fallback.

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.
02271264300
phone-callphone-call