Exposing GraphQLs as Managed APIs

  • By Hiranya Abeyrathne
  • 12 May, 2020

Executive Summary

GraphQL is a query language for web based APIs developed by Facebook. Many developers tend to think that GraphQL can be used as a replacement for REST due to its popularity. This article explores the following in detail:

  • GraphQL basics and key characteristics
  • Values of GraphQL over REST
  • Importance of an API Gateway for GraphQL services
  • Benefits of exposing a GraphQL service as a managed API via WSO2 Gateway

Due to the many advantages of GraphQL - exact data fetching, versionless API evolution, single API call data fetching, strongly type system - we believe that it is a better option for data intensive backend services, particularly if they have been exposed with API management benefits such as security, rate limiting, monitoring, and monetization.

Introduction

GraphQL is a leveraging API technology that declares a query language for your APIs. It was created by Facebook in 2012 to power their mobile applications. In 2015, an open source version of GraphQL was released and it has been revolutionized by several innovative companies such as GitHub, Spotify, Yelp, Coursera, and Shopify because of GraphQL’s ability to make API calls more efficient, flexible, and developer-friendly. The advantage of this is that the results returned from GraphQL queries can always be predictable - you get what you request for, nothing more and nothing less.

Similar to the OpenAPI definition in REST APIs, the heart of every GraphQL specification is a Schema Definition Language (SDL) which is a graph data model. It specifies what types of data are available and the relationship among them in order to create a contract between the server and the client. GraphQL has three root types that are referred to as primary operations. The SDL file is the place where backend developers can define the list of operations under these three types to show what queries are allowed to be made by the client:

  • Query for reading data
  • Mutation for writing data
  • Subscription for automatically receiving real-time data over time

While the above root types can be identified as the entry point types of a given query, every field has either a primitive type (such as int, string, float, boolean, etc.) or a complex type (object types and enums). Resources are meaningless for GraphQL APIs. It has the flexibility to extend the endpoint by adding new abilities to the data model without breaking the existing changes. This allows you to have a versionless API for GraphQL as described in the GraphQL documentation.

Let’s consider a simple ProductsManagement GraphQL service that provides the ability to add a product, to remove a product, and to get the list of products available for a retailer and their customers.

Use case: A retailer has multiple products in his store where customers can order by referring to the list of products available.

ER Diagram of ProductsManagement Service

Exposed operations for:

  • Customer: allProducts, addCustomer, product, customer
  • Retailer: allCustomers, allProducts, addProduct, removeProduct

“ProductsManagement” GraphQL SDL looks like this:

schema {
  query: Query
  mutation: Mutation
}

enum Category {
  CLOTHING
  FOOTWEAR
  COSMETICS
}

type Customer {
  id: ID!
  name: String!
  description: String
  product: [Product]
}

type Product {
  id: ID!
  name: String!
  description: String
  category: Category!
  customer: [Customer]
}

type Query {
  product(id: ID!): Product
  customer(id: ID!): Customer
  allProducts: [Product]
  allCustomers: [Customer]

}

type Mutation {
  addProduct(name: String!, category: Category!): Product
  addCustomer(name: String!): Customer
  removeProduct(id: ID!): Product
}

SDL - ProductsManagement Service

Example queries and mutations can be given as below for the above GraphQL.

Sample Use Case Sample Request Sample Response
A customer retrieves the list of available products
query {
  allProducts {
    id
    name
    description
    category
  }
}
{
  "data": {
    "allProducts": [
      {
        "id": 1,
        "name": "T-Shirt",
        "description": "Blue Color T-Shirt",
        "category": CLOTHING
      },
      {
        "id": 2,
        "name": "Sandals",
        "description": "Ladies Sandals",
        "category": FOOTWEAR
      },
      {
        "id": 3,
        "name": "Frock",
         "description": "Red Color Frock",
        "category": CLOTHING
      }
    ]
  }
}
A retailer adds a new product to the list
mutation {
  addProduct(
    name: "Leather Shoes"
    category: FOOTWEAR
  ) {
    id
    name
  }
}
{
  "data": {
    "addProduct": {
      "id": 4,
      "name": "Leather Shoes"
    }
  }
}
A retailer retrieves the customer list for a particular product
query { product (id: 2) { 
  id
  category
  customer {
    name
  }
}}
{
  "data": {
    "product": {
      "id": 2
      "category": FOOTWEAR,
      "customer": {
        "name": "John"
      }
      "customer": {
        "name": "Tom"
      }
      "customer": {
        "name": "Jane"
      }
    }
  }
}

REST vs GraphQL

To provide a clearer understanding of GraphQL, I will take an in-depth look at REST and GraphQL.

No more over-fetching or under-fetching

REST endpoints are known for doing either over-fetching or under-fetching where it returns more or less information than requested for by the client, creating a need for another request. GraphQL solves over-fetching and under-fetching by returning what is exactly asked for in a single request.

Single API call data fetching

The REST service provides individual endpoints for each resource and when a developer wants to collect all the required data, he/she has to send multiple requests to different endpoints. This differs in GraphQL as it exposes data via a single endpoint and focuses mainly on the task itself, enabling the developer to fetch the requested data with one API call. For example, consider the same ProductsManagement service example, where the retailer wants to know the list of customers who ordered a particular product. If you try this with REST, you need to do the following API calls:

  • Fetch a list of all the products (/products/)
  • Fetch the list of customers’ Ids sending the particular product Id (/products/{orderedProductId}/customers)
  • Fetch the customer specific details by sending their Ids one by one (/products/{orderedProductId}/customers/{id})

But with GraphQL, you can get the details of a customers’ list for the particular product with just one call as shown below:

Auto generated documentation

GraphQL generates documentation using a tool called GraphiQL and syncs them with the API changes. As GraphQL APIs are tightly coupled with the code, when a field, type, or a query changes, so does the documentation. This gives the developers a huge advantage as they do not have to spend much time documenting an API. This is different to the REST APIs, as developers have to follow a variety of documentation standards and specifications when documenting REST APIs.

REST API documentation explains individual endpoints with their functionality and the parameters to be passed. GraphQL documentation describes data types, fields, and the relations between them, tailoring developers to send requests to get what they are exactly looking for.

Versionless API evolution

Most APIs do versioning when there is limited control over the data that returned from an API endpoint. Any change can break the API and in order to cope with these changes, it requires a new version. It introduces an overhead of doing often releases and on the other hand, incremental versions can have a negative aspect on understanding and maintaining an API. REST has no clear rules on versioning and allows you to keep multiple versions of APIs whereas GraphQL takes a strong non-versioned approach by providing the tools for the continued evolution of a GraphQL schema. GraphQL only returns the data that is exactly requested, so new features can be added via new types and fields to the same schema without introducing any breaking changes.

Performance

On the subject of performance, both REST and GraphQL have their own pros and cons depending on the situation. Consider a data fetching network that consists of movies related data. With REST API, you will need 3 API calls to retrieve the details of 3 movies. However, a single API call would work for GraphQL to retrieve the same information. On content delivery networks, web cache or reverse proxy plays a major role in REST APIs. Return cached responses result in a high performance over the GraphQL APIs which return exactly what is requested for by calling to the backend server.

Note that GraphQL can encounter performance bottlenecks if a client requests for too many nested fields at once. In such situations, it’s better to use REST APIs. Although it might introduce multiple network calls, which can also take up a considerable amount of time, it will be safer from the server-side maintenance.

Schema and type system

In GraphQL, once the schema is defined, developers working on the front-end and back-end work independently without further communications since both parties are aware of the definite data structure sent over the network. This provides the ability to test their applications by mocking the desired data structures while doing front-end development.

Changes to the front-end applications can be done without any extra work on the server-side as the clients can specify their exact data requirements on the client-side itself, without back-end developers having to do adjustments on the server-side. This provides the ability to build APIs that evolve easily. This is different in REST, as the front-end and the back-end are tightly coupled. Therefore, changes to the client-side design require changes to the server-side design as well. This issue can be solved using solutions such as an API Gateway.

Importance of an API Gateway for GraphQL Services

GraphQL is a strongly typed system, capable of retrieving multiple and nested level data solving the over-fetching or under-fetching problem as described here. GraphQL API enables developers to tailor requests to retrieve information needed and construct the query requests to pull data from multiple data sources in a single API call. Using GraphQL is great for consumers as they have more power and freedom to construct complex types of queries and pass to the backend directly, compared to REST.

In spite of the convenience to consumers, the execution of such GraphQL queries introduces more work on the backend (GraphQL) server as it needs to resolve them by filtering and fetching the data. Hence, depending on the query, it may create an overhead on the server to process the query. This is a major drawback in GraphQL. If your API is not publicly consumed and you are not fielding an infinite number of query variations, then passing such queries directly to a GraphQL interpreter at the backend is arguably an overkill and harmful even if it allows a client to ask for any possible resource and field combination.

A way out of this drawback is to make use of rate-limiting and authorization mechanisms for stopping inefficient requests including maximum query depths, query complexity weights, avoiding query recursion, and query persistence capabilities. Typically most backend services have cross-cutting concerns on authentication, authorization, and rate-limiting. If the backend developer implements these on their backend, it becomes a repetitive process and introduces maintenance complications. For GraphQL services, it is straightforward and worthwhile if it has another layer to take care of all of these aspects and manage the GraphQL service while the GraphQL backend is only focusing on its business logic.

This is where the exact requirement of GraphQL API management acts as a crucial solution to tackle these complications. It makes sense to expose the GraphQL APIs via an API Gateway which routes all requests to the designated endpoint, taking into consideration values defined for the operations and fields that are included at the requested GraphQL queries, mutations, and subscriptions. Along with these core capabilities, it can bring various other advantages to your organization such as providing a platform to discover GraphQL APIs documenting APIs and to expose a user friendly try-out console for the GraphQL service which is easier to use than your backend service.

Therefore, while GraphQL is focusing on what it does best, it is important to make sure that the GraphQL services are exposed in a secure, controlled, monitored, and monetized environment. WSO2 API Gateway fulfills the aforementioned API management requirements for GraphQL APIs. Let’s look at the values that we can provide for query operations by exposing “ProductsManagement” GraphQL service as the managed “ProductsAPI” through WSO2 API Gateway.

Exposing GraphQL APIs via WSO2 API Gateway

Registering GraphQL APIs on the platform

GraphQL services can be deployed via any API management platform as yet another REST service by adding two resources - GET (for queries) and POST (for mutations). But GraphQL API developers will not be satisfied with REST API features to manage their GraphQL services as they are incapable of fulfilling the exact GraphQL requirements. Even though the REST requests are provided with the resource level API management capabilities in REST APIs, GraphQL requests should focus on the query payload. Therefore, it is essential to have first class support for GraphQL APIs to serve the GraphQL specific requirements for API developers.

WSO2 API Manager Publisher provides the ability to deploy a GraphQL service as a managed API supporting the GraphQL requirements. The API developer can create a GraphQL API easily by importing its SDL file. For example, consider the same ProductsManagement service. An API developer can deploy it as the ProductsAPI via WSO2 Publisher as shown below:

ProductsAPI - WSO2 Publisher

Discovering and consuming GraphQL APIs

Once GraphQL API developers publish and expose their APIs as managed GraphQL APIs, a consumer's life becomes very easy if there is a portal that has hosted and advertised those exposed APIs for consumers to self register, discover, evaluate, subscribe, and consume.

Benefits such as viewing a list of available GraphQL APIs, selecting APIs under different categories, searching GraphQL type APIs, finding details of an API, viewing or downloading the SDL and documentation of an API, rating and commenting on consumed APIs, and facilitating a developer friendly try-out tool help GraphQL application developers to understand APIs’ information and try out the queries in a secured environment. The WSO2 API Manager Developer Portal comes with the afore-mentioned benefits. It has been integrated with a GraphiQL try-out console that provides a list of useful features:

  • Syntax highlighting
  • Intelligent type ahead of fields, arguments, types, and more
  • Real-time error highlighting and reporting for queries and variables
  • Automatic query and variables completion
  • Automatically adds required fields to queries
  • Documentation explorer and search with markdown support
  • Query history using local storage
  • Run and inspect query results using any promise that resolves JSON results
  • Support for the entire GraphQL Language Specification (queries, mutations, subscriptions, fragments, unions, directives, multiple operations per query, etc.)

ProductsAPI - WSO2 Developer Portal

Authentication for GraphQL APIs

APIs are mostly exposed to external users who are not a part of the system. In an API ecosystem, there are at least three types of parties involved - API developers, app developers (API consumers), and end-users who use apps that consume the GraphQL APIs. First, an API developer develops the ProductsAPI and makes it available to external parties. Next, a GraphQL application developer (an API consumer) discovers a particular GraphQL API in the Developer Portal and subscribes to the ProductsAPI after which the application developer can consume the ProductsAPI. Finally, end-users consume the ProductsAPI via the subscribed application by delegating access to the application to access the API on behalf of them.

Security plays a major role at this point as it is crucial to ensure that users who access API operations are authentic. Instances may occur where some GraphQL API operations are exposed to the public, giving access to anyone without authenticating to the system. At this time, security for these operations can be disabled. For instance, allProducts operation in ProductsAPI, which gets details of all listed products, should be accessible by anyone. WSO2 API Manager facilitates API developers to enable or disable operational level security at the Publisher as follows. (Note that the allProducts operation hasdisabled security.)

Operational Level Security

In this scenario even though the user can retrieve all products without using an access token with the following query:

query {
  allProducts {
    id
    name
    description
    category
}}

If the query contains customer details (as shown below), the allProducts operation cannot be accessed without an access token since security is enabled for the customer operation.

{
allProducts {
  id
  customer{
    name
  }
}}

API developers can add multiple authentication schemes at the runtime configurations that facilitate the selection of a variety of authentication mechanisms such as OAuth2, OpenID Connect, API Keys, Basic Authentication, and Mutual TLS Authentication.

Authentication Level Security

Authorization for GraphQL APIs

Typically, any system such as a Content Management System (CMS) or a Human Resource Management System (HRMS), has several user groups that are assigned for specific tasks and responsibilities. Operations defined at the Graphql SDL need to be accessed by only a subset of users in an organization. Due to the expanding consumer base, GraphQL API developers have to focus on limiting the API access to make sure that only the authorized parties have access to a particular operation.

WSO2 API Manager can assign different levels of permissions to GraphQL operations for fine grained access control with OAuth2 scopes. This enables the API Gateway to inspect the incoming query and decide whether to allow or block the call. For example, consider a requirement of an API developer who is going to expose the “ProductsAPI” and needs to distribute the privileges among the involved user groups to access the API. In this scenario, we can identify two types of user groups:

  • End users who act as the Retailer
  • End users who act as the Customer

The operations to add/remove a product and view all customer details should only be opened to the retailer user group. Similarly adding customer operation should be accessible only to the customers. To cater to this business logic an API developer can grant permissions while doing role based access control over the operations. Suppose there are two scopes defined as “retailer” and “customer” which are bound to the roles mapping to each user group. An API developer can assign the scopes in the operation level and the particular user groups can access only the allowed operations.

Operational level Scopes - ProductsAPI

Rate limiting GraphQL operations

It can be expensive to execute some operations on the server and various combinations for a particular element can be referred to in queries. Therefore, allowing the same rate limits to all the operations or treating all of them, in the same manner, will not be a good idea in a production system. There must be a mechanism through which API developers can recognize these expensive operations and assign rate limits to control the API traffic accordingly.

WSO2 API Manager enables you to manage operational level rate limits. API developers can set rate limits by individual GraphQL operation (or for the entire API) to manage operation based traffic. For example, consider ProductsAPI. Anyone should be able to fetch the list of products that are available in the store limitlessly. Therefore, the “allProducts” operation should have the Unlimited rate limit. Furthermore, both retailers and customers must have the ability to retrieve user information when fetching the list of products, product details, and customer details. There are multiple ways to reach the “customer” element and the API developer can assign a comparatively high rate limit (e.g. 50k/min) on the customer operation. Since updating product details is an expensive task, the API developer should assign a lower rate limit compared to the other operations (e.g. 10k/min) to avoid any database bottlenecks.

Operational Level Rate Limiting Support

Rate limits specific to GraphQL queries

Since the client has the freedom to request for any amount of data from the server, a malicious request with nested queries can stress the server if repeated at scale. For example, a cyclic query that is common in GraphQL can be defined as follows:

{
allProducts {
  id
  Customer{
    Product {
       id
       Customer{
         id 
         Product 
          ...
       }
     }
    name
  }
}}

Therefore, the depth can grow exponentially till infinity() and damage the server.

Also, since a single complex GraphQL call could be an equivalent of thousands of REST requests, the potential to overwhelm the server even under the correct conditions exists; there can be certain app-specific queries which are neither deeper nor with many object elements but still become very expensive. To prevent this, the API gateway can execute the query analyzing process that calculates the query complexity and depth limit before the request passes to the backend. If a request exceeds a certain limit or the maximum complexity, the API gateway can take the decision to block the request.

This feature will come as an alternative rate limiting feature with the next release of WSO2 API Manager. As the maximum query complexity and the depth are dependent on use cases of applications, the decision on selecting the maximum depth limit and complexity rests with application developers, while allowing them to pick the complexity plans when subscribing to the API. This will limit transactions per consumer and protect the backend.

Operational level analytics

Facilitating fine-grained insights on the query data that are requested on the GraphQL backend is a crucial API management requirement. This is because API developers allow low-level performance monitoring of requests that are processed by the GraphQL server easily.

WSO2 API Manager supports this platform to view the operational statistics on GraphQL APIs. Important parameters such as traffic and latency on each aggregate operational query and individual operational queries are displayed on the analytics dashboard. Multiple operations can be selected from the defined operation list and we can check how parameters are getting varied for the requests with a combination of operations. Moreover, this gateway support can be extended easily by integrating with other analytics tools such as ELK and Splunk.

GraphQL in Action

You can view a live demo of GraphQL APIs in action in this recorded webinar.

Conclusion

GraphQL is an emerging query manipulation language for APIs, which is an open source runtime for querying and retrieving existing data in an optimal manner making applications more efficient. Because of developer convenience, GraphQL is increasingly used today. It is not a replacement for REST, but you can use both at the same time depending on the situation as GraphQL is a query language for your API, while REST is an architectural style that defines a set of constraints for your application.

While GraphQL focuses on what it does best, backend developers continue to worry about ensuring that GraphQL services are exposed in a secure, controlled, monitored, and sometimes even in a monetized environment where WSO2 API Gateway is useful as a middle layer to expose GraphQL services in a managed environment. We’re in the process of introducing more features to WSO2 API Manager to take GraphQL APIs to the next level. If you have any questions, get in touch with us via Slack and our API management experts are happy to help.

About Author

  • Hiranya Abeyrathne
  • Software Engineer
  • WSO2