The developers'

API Guide

Everything you need to know about building, purchasing and integrating with APIs.

GraphQL API

What is a GraphQL API?

A GraphQL API is an API that follows the GraphQL specification. GraphQL is a query language for APIs that revolutionizes how clients interact with servers by allowing them to request only the needed data, offering flexibility and efficiency in data retrieval. This flexibility significantly shifts from traditional API approaches like REST, where the server defines the data returned in response to specific endpoints.

Where did GraphQL originate?

In 2012, Facebook faced a problem: they wanted to improve the functionality of their news feed, but they were pushing the limits of their mobile app’s ability to download frequent, small updates. REST was not a viable solution because the structure of their internal data models didn’t follow REST conventions. They also required high bandwidth efficiency since they were dealing with mobile devices that don’t always have a reliable internet connection. Enter GraphQL, which they eventually released as open source in 2015.

GraphQL solves two major problems. First, it enables API consumers to structure their requests in a way that mirrors the response, both in the hierarchy and in the returned data. This makes it easier to return only the data you need, unlike REST, which returns all available data, thus taking up precious bandwidth. Second, REST requires API providers to build functions for each specific query and filter they want to support. GraphQL, on the other hand, supports queries and filters for all data fields by default. 

GraphQL APIs key characteristics

GraphQL APIs are defined by several key characteristics that set them apart from traditional RESTful APIs. These characteristics include:

  • Data precision: Clients can request only the specific data they need, eliminating over-fetching and reducing the amount of unnecessary data transferred over the network.
  • Reduced data transfer: GraphQL minimizes the amount of data transferred between the client and server by allowing clients to request only the fields they require. This optimized data transfer improves overall performance.
  • Batching: GraphQL enables clients to receive multiple server responses in a single request. This feature reduces the number of round-trips between the client and server, further enhancing performance and efficiency.

GraphQL APIs use cases

Based on the use case of Facebook, you might have already guessed that GraphQL is best for use cases where something needs to make frequent updates that include only a specified set of data. This makes it great for mobile applications since bandwidth is precious and unreliable. Any unused data from a REST response is wasted bandwidth, and GraphQL helps you avoid this.

Quite a few companies jumped to GraphQL, evidenced by all of the companies that joined the GraphQL Foundation. GitHub is one notable company that released v4 of their API with GraphQL; they give the following reasoning:

“GitHub chose GraphQL for our API v4 because it offers significantly more flexibility for our integrators. The ability to define precisely the data you want — and only the data you want — is a powerful advantage over the REST API v3 endpoints. GraphQL lets you replace multiple REST requests with a single call to fetch the data you specify.”

Shopify released a GraphQL API in 2018 to make it easier to build and manage online storefronts, they like it because it helps reduce requests that are made to their API servers:

“GraphQL gave us big wins by reducing the number of round trips made to the server and giving us a well-defined and strongly typed schema to work against. GraphQL has also played a major role in helping us find and fix bugs before impacting our merchant’s daily operations.”

Yelp released its first GraphQL API in 2017 and liked it for how easy it is to access relational data:

“GraphQL also makes traversing graphs (and therefore relational data) very easy. Unlike most REST APIs, you don’t need to make multiple requests to pull relational data. Based on the schema, you can retrieve data based on the relations they have. For our API users, this means easy access to all the great business information we have available.”

GraphQL APIs advantages

APIs using GraphQL offer several advantages that make them a compelling choice for modern application development:

Reduce bandwidth requirements

Let’s take a look at an example that demonstrates this difference. Say we want to build an app that keeps track of our best friend’s birthday, and we want to access data that represents the date of their birthday and their favorite food, which we’ll use to buy them a gift.

The typical REST request would look like this:

curl -X GET 'https://api.nylas.com/messages/{message_id}'

The REST response would look something like this:

{
  "id": "8f642b0g8cdbf07b",
  "subject": "Meeting Schedule",
  "from": [{"name": "Alice Johnson", "email": "alice@example.com"}],
  "to": [{"name": "Bob Brown", "email": "bob@example.com"}],
  "date": 1609459200,
  "body": "...",
  "attachments": [...]
}


For a Nylas-specific context, we can adapt the example to fit into the realm of email, calendar, or contacts, which are core Nylas products. Let’s consider a scenario where you’re developing an application that needs to retrieve an email’s subject and sender but not the entire email content or attachments, which could be substantial. This is a situation where GraphQL shows its strength by allowing you to request just the data you need.

REST API Example with Nylas

For a RESTful API call, you might request details of an email like so:

bashCopy code

curl -X GET 'https://api.nylas.com/messages/{message_id}' \ -H 'Authorization: Bearer {ACCESS_TOKEN}'

This request could return a lot of information about the email, much of which might not be relevant to your immediate needs:

jsonCopy code

{ "id": "8f642b0g8cdbf07b", "subject": "Meeting Schedule", "from": [{"name": "Alice Johnson", "email": "alice@example.com"}], "to": [{"name": "Bob Brown", "email": "bob@example.com"}], "date": 1609459200, "body": "...", "attachments": [...] }

In this example, you get the entire email object, including the body and attachments, which might be unnecessary for your application.

GraphQL API Example with Nylas

With GraphQL, you could make a more focused request:

query {
  email(id: "8f642b0g8cdbf07b") {
    subject
    from {
      name
      email
    }
  }
}

And the response would contain only the requested fields:

{
  "data": {
    "email": {
      "subject": "Meeting Schedule",
      "from": [{"name": "Alice Johnson", "email": "alice@example.com"}]
    }
  }
}

This GraphQL request specifies exactly what information you want to retrieve (subject and from), leading to a smaller, more efficient response. This results in significant bandwidth savings, particularly for objects with tens or even hundreds of data points. Even more so when you multiply that across millions or billions of API requests.

Manage complex data structures

GraphQL is also good for situations where you need to implement user interfaces that include complex data structures. Imagine you’re developing an application that not only retrieves specific details about a calendar event (like a meeting) but also wants to gather extra information related to the event, such as participant details and their contact information. This situation demonstrates GraphQL’s strength in handling complex data structures through nested queries, allowing for a more efficient data retrieval process compared to traditional RESTful APIs.

REST API Example with Nylas

With a RESTful approach, you might first retrieve the calendar event and then make additional requests for each participant to get their contact information. This could look something like:

curl -X GET 'https://api.nylas.com/events/{event_id}' \
     -H 'Authorization: Bearer {ACCESS_TOKEN}'

Response:

{
  "id": "event_12345",
  "title": "Team Meeting",
  "participants": [
    {"email": "alice@example.com"},
    {"email": "bob@example.com"}
  ]
}

You’d then need separate requests for each participant to get more details:

# Requests to get contact details for each participant
curl -X GET 'https://api.nylas.com/contacts?email=alice@example.com' \
     -H 'Authorization: Bearer {ACCESS_TOKEN}'

curl -X GET 'https://api.nylas.com/contacts?email=bob@example.com' \
     -H 'Authorization: Bearer {ACCESS_TOKEN}'

This approach requires multiple network requests, increasing complexity and potential latency.

GraphQL API Example with Nylas

A GraphQL implementation might allow you to make a single, nested query to retrieve the event along with detailed information about each participant in one go:

query {
  event(id: "event_12345") {
    title
    participants {
      email
      contact {
        name
        phone
        organization
      }
    }
  }
}

And the response could look like this, assuming a hypothetical GraphQL service over Nylas data:

{
  "data": {
    "event": {
      "title": "Team Meeting",
      "participants": [
        {
          "email": "alice@example.com",
          "contact": {
            "name": "Alice Johnson",
            "phone": "555-0100",
            "organization": "ExampleCorp"
          }
        },
        {
          "email": "bob@example.com",
          "contact": {
            "name": "Bob Brown",
            "phone": "555-0200",
            "organization": "AnotherExampleCorp"
          }
        }
      ]
    }
  }
}

This single GraphQL query efficiently replaces multiple REST calls, simplifying the client-side logic and potentially reducing the bandwidth and time needed to gather the same information.

It’s important to note that while this example illustrates the advantage of using GraphQL for managing complex data structures and relationships, Nylas primarily provides RESTful APIs. The GraphQL example is hypothetical and would require additional infrastructure to translate GraphQL queries into Nylas API calls or the existence of a GraphQL layer built on top of Nylas services.

GraphQL APIs disadvantages

As we’ve seen with SOAP and REST, there are always trade-offs with any API interface. In certain situations, these may become big enough downsides to seek an alternative. 

Caching is more complex

REST APIs benefit from caching provided as part of normal HTTP server functionality. In other words, you can generally rely on modern HTTP servers and clients to properly cache GET requests made to a REST API. GraphQL uses a single URL for all requests, which often causes caching not to perform as expected. 

Fortunately, there are open source tools that help you resolve this. On the server side is DataLoader, a NodeJS utility that can be used for batching and caching. On the client side, you can use Apollo Client, a JavaScript library that includes caching for requesting GraphQL APIs. Either way, you need to make sure that you’ve fully evaluated whether your app requires caching and ensure you plan accordingly.

Queries require optimization

GraphQL relies heavily on relationships between data objects, and as with any other queries on relational databases, it’s possible to write inefficient GraphQL queries. As your requests dive further into a data graph, the number of API requests you make can rise very quickly. If you’re producing a GraphQL API, you need to make sure you properly use batching where possible to group actions that access the database. You can also implement depth limits that prevent requests from going too far into a data graph. As an API consumer, it’s important to consider the complexity of the data requests you make and determine if there are ways to optimize them.

Creates potential to expose a data model

One powerful feature of GraphQL is introspection, which lets you ask a GraphQL schema what queries it supports. This makes developing GraphQL APIs much easier but can also open you up to exposing your internal data models if this feature is enabled in your production servers. Fortunately, there are ways to restrict what schema is available to users or disable the feature entirely. You must ensure your API isn’t exposing sensitive data with GraphQL’s introspection function.

GraphQL APIs components

GraphQL APIs have several key components that work together to enable efficient data retrieval and manipulation.

Schema

The schema is the fundamental contract between clients and servers in a GraphQL API. It defines the types, queries, mutations, and relationships available in the API, acting as a blueprint for how data can be requested and manipulated.

  • It specifies the types of objects that can be queried or mutated, including their fields and relationships.
  • Types in the schema can be scalar (e.g., Int, String, Boolean) or custom-defined (e.g., User, Product), and they can have fields that represent the data attributes.
  • The schema is typically defined using the GraphQL Schema Definition Language (SDL), making it human-readable and easy to understand.
  • It acts as a roadmap for clients, guiding them on what data they can request and how to interact with the API.

Queries 

Queries are how clients request data from a GraphQL API and allow clients to specify the exact data they need.

  • Queries are one of the fundamental operations in GraphQL and are used by clients to request data from the server.
  • Clients define their queries based on the schema, specifying the fields they want to retrieve, any arguments for filtering or sorting, and the structure of the response.
  • GraphQL queries can be as simple or as complex as needed, allowing clients to request nested data structures with precision.
  • Query execution begins at the “root” query type defined in the schema, and the server resolves each requested field by invoking the corresponding resolver function.

Mutations

Mutations are used to modify data by providing clients with a way to create, update, or delete records while ensuring data integrity and consistency.

  • While queries are used for fetching data, mutations are employed for modifying data on the server.
  • Mutations allow clients to create, update, or delete records.
  • Similar to queries, mutations are also defined in the schema, specifying input types for the data to be modified and the expected output.
  • Mutations are executed sequentially, ensuring data integrity and consistency on the server.
  • Like queries, mutations also have resolver functions that define how the data changes should be processed.

Resolvers

Resolvers power a GraphQL API. They are responsible for fetching and processing data for each field in a query or mutation, determining how data is retrieved and transformed.

  • Resolvers are the heart of a GraphQL API, which determines how data is fetched and returned for each field in a query or mutation.
  • Each field in the schema has an associated resolver function specifying how to retrieve that field’s data.
  • Resolvers can interact with databases, external APIs, or any data source to fetch the requested information.
  • They can also perform data transformations or computations before returning the data to the client.
  • Resolvers are organized according to the schema’s structure and execute hierarchically, ensuring that data dependencies are resolved correctly.

How to use GraphQL APIs

Following the steps below, you can effectively use GraphQL API in your applications, ensuring efficient data retrieval, security, and adaptability as your project evolves.

Step 1 – Installing a GraphQL client

Install a GraphQL client library or tool that matches your programming language or platform. Common GraphQL clients include Apollo Client, Relay, and GraphQL Request.

npm install @apollo/client

Step 2 – Initializing the client

Initialize the GraphQL client by providing the API endpoint or URL where the GraphQL server is hosted.

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://example.com/graphql', // Replace with your API endpoint
  cache: new InMemoryCache(),
});

Step 3 – Writing a query:

Create a GraphQL query that specifies the data you want to retrieve. Use the query language to structure your request.

import { gql } from '@apollo/client';

const GET_USER_PROFILE = gql`
  query GetUserProfile($userId: ID!) {
    user(id: $userId) {
      id
      username
      email
    }
  }
`;

Step 4 – Sending the query:

Use the GraphQL client to send the query to the API endpoint. Include the query as a string in the request.

import { useQuery } from '@apollo/client';

const { loading, error, data } = useQuery(GET_USER_PROFILE, {
  variables: { userId: '123' }, // Pass query variables
});

Step 5 – Receiving and processing the response

Receive the JSON response from the API. The response will contain the data requested in the query.

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;

Step 6 – Error handling

Implement error handling to handle any issues that may arise, such as network errors or GraphQL-specific errors returned in the response.

Step 7 – Displaying data

Once you have received the data, display it in your application’s user interface according to your design and user experience requirements.

<div>
  <p>Username: {data.user.username}</p>
  <p>Email: {data.user.email}</p>
</div>

Step 8 – Caching and optimizations

Implement caching mechanisms to store frequently requested data on the client side, reducing redundant requests. Optimize queries and mutations for performance by avoiding over-fetching or deep nesting.

Step 9 – Testing and validation

Test your queries and mutations to ensure they return the expected results. Use GraphQL developer tools like GraphiQL or GraphQL Playground for interactive testing and exploration.

Best practices to build a GraphQL API integration

To ensure a successful integration of GraphQL APIs, consider the following best practices:

  • Understand the schema: Familiarize yourself with the API’s schema, which defines the types, queries, mutations, and relationships available. Explore the documentation to gain insights into what data is accessible and how to structure your queries and mutations.
  • Plan your queries and mutations: Clearly define your data requirements and the operations you need to perform. Use the flexibility of GraphQL to create precise queries tailored to your application’s needs. Consider pagination, filtering, and sorting options when retrieving data.
  • Security considerations: Authenticate and authorize users appropriately, following best practices for securing GraphQL APIs. Employ rate limiting and query complexity analysis to prevent abuse.
  • Versioning and deprecation: Stay aware of API versioning to ensure backward compatibility as the API evolves. Pay attention to deprecated fields or types and update your queries accordingly.
  • Monitoring and performance optimization: Monitor your GraphQL API’s performance, tracking query execution times and resource usage. Optimize resolvers and data fetching to address any performance bottlenecks.
  • Documentation and collaboration: Keep API documentation up to date for your team and other developers who may interact with the API. Collaborate with backend teams to understand schema changes and upcoming features.

GraphQL APIs offer a flexible and efficient way to interact with data in modern applications. By understanding their key characteristics, use cases, advantages, and potential challenges, developers can harness the full potential of GraphQL to build dynamic and responsive applications. Following best practices ensures a smooth integration experience and optimal performance.