devices

Unifying APIs with Hasura remote schemas

Introduction

If you haven't heard of Hasura before it's a backend technology that gives you an instant API layer over your database, it also provides other features common to API layers such as authentication, hooks, permissions and much more. The advantage of Hasura is that you don't need a backend engineer to write each end point, the API is inferred from the tables, views, relationships and functions it has access to so in a matter of minutes you have a full featured API.

We started utilising Hasura on a couple of projects a few years ago and as developer that primarily works on the front end I found it liberating, it not only allowed us to have an instant rich unhindered GraphQL API but also reduced reliance on the backend work being laid out ahead of the front end work. We used the Hasura console to build the database schema the UI needed as it was built rather than the all too common back and forth ritual asking others for new data structures, endpoints and services.

Solving problems

When creating modern web apps it's not uncommon to utilise several APIs whether that's first, third or even second party, where some of this data is then combined or even chained to get subsequent data to display to the user. This can sometimes mean you utilise different API libraries, write your own or just do the occasional AJAX request, you may also need to keep track of different authentication tokens and even account for case and structure varience in the responses.

There are a couple common issues that often arise that have unfortunately become part of day to day development.

  • Chaining requests on the front end between different APIs, ensuring the data is valid at each step and then handling errors at each stage.
  • Dealing with varying API structures, quirks and formats on the front end either by converting to known types or by assumed knowledge/documentation.
  • Handling more than one authentication mechinism either on the front end or creating a backend service that keeps everything in line.
  • Composing data structures from different sources on the front end (Redux for example).

Let's talk about how Hasura can help you deal with this by using remote schemas for each of these points.

Chaining requests

This is usually done because the data is not closley related from the back end perspective but may be from the front end.

A typical example would be a view that shows the related posts for a post category this useually involves geting the individual post first then subsequently get the related posts once you have that posts category.

Because of the nature of GraphQL if the relationships exist in the schema you can execute a single deeper query when it makes sense (use with caution), Hasuras remote schema feature extends this abillity to other APIs and ties it back into a single response.

Although deep relational queries can be really powerful it shouldn't be used a way to get large chunks of unrelated data all at once. It's often best to keep data reasonably flat and use this where it makes reasonable sense, each case is different and there's many considerations in how to break up requests but one factor I tend to keep coming back to is by view or component, for example a page with 3 tabs may have a request per tab view or if one component handles more than a couple of data properties it might be better that it has a specific request.

It's not uncommon to have recursive relationships and many new commers to GraphQL are rightly concerned about this being abused and slowing down the database however in Hasura the depth of the request can be managed with the "Allow list: or if using Hasura cloud there's a depth limit that can be configured.

Dealing with varying API responses

It's inevitable when working with many APIs in a single project that not all responses will share the same structures, conventions and casing. There are a few ways of dealing with this but usually classes/types are created to transform the response into consistent predictable data that can be tested, this step may also be used to filter only the data that's needed as REST APIs often return more than what's needed by the client.

By using Hasura remote schemas with GraphQL this is dealt with in 2 parts, the nature of how GraphQL works means it only ever returns what you request in the structure you request it. When creating remote schemas for your 3rd party services you have complete control over how this is represented, usually making other APIs conform to Hasuras default conventions such as snake_case, using id as a primary key etc although this is not enforced so you have some flexibility here. So this allows you do deal with these issues before it reaches the client and keeps this part of your logic separate from the front end codebase.

Handling multiple authentication mechinisms

Occasionally when calling different 3rd party services from the front end you will also need to maintain different authentication mechanisms, whether it's a short lived JWT or single persistent key to access something like a weather API. This is paired with either a client library or just plain AJAX calls to the services API. With Hasura Remote schemas you can construct them in such a way where you only have a single API and a single authentication mechanism by storing keys as environment variables in Hasura and using JWT token claims to identify different users. This again in many cases can unify APIs and abstract complexity away from the client.

Composing data structures from different sources

If you have closely related information that comes from different sources it's not uncommon to merge this into a new structure to consume for example a restaurant location may be fetched from one API then the current weather from another, this may then be combined because the view shows this information together, even if they're not literally combined into a new model they are often indirectly combined my passing them as properties to components for example:

<RestuarantView restaurantData={restaurantData} restuarantWeather={restuarantWeather} />

// props = { restaurantData, restaurantWeather };

Sometimes projects will try to abstract this away from the user by using a middle service or extending their existing API to handle requests, for example google translate needs to be called from a hosted service, which usually exposes an API for the consuming client (Googles way of pushing the responsibility of handling tokens/security over to you). The implementation of this can vary depending on the API framework (if used), and could either be closely tied to existing data or completely separate.

When doing this in the front end there is often a unspoken expectation that the developer knows that these are two different data sources which can cause unexpected issues especially if the structures aren't consistent between the merged data. By composing at the API level using remote schemas this complexity can be removed from the front end so that the API can be treated as a single coherent service.

Other benefits

By isolating these parts of the development process you gain many of the same benefits you would from typical code abstraction such as:

  • Clearer more concise test seperation.
  • Being able to run the schemas in isolation
  • Separate codebase and management by other members of the team.
  • Reuse of schemas between projects for example any of googles services, S3 etc.

Use cases

Large Legacy System

Our first use case for this was a way of augmenting an existing large legacy database and legacy APIs for modern apps without compromising the existing system which was relied upon by a vast global team. It's not uncommon for companies digital aspects to scale very quickly and the underlying technology isn't updated but just "maintained" sometimes for 10 years+, as a result their data was now very muddy with different campaigns and features have been bolted on over the years, even with a team of people working on it for a few months it was very difficult to comprehend and we'd often need to rely on a single person to help demystify certain parts and they themselves were hesitant at best on confirming our findings. With Hasura we could isolate the data we need, sync it with a separate database and extend it with data that was more related to the new applications essentially giving us a fresh start, when any core data that needed to be modified would be synced back to the main database.

This helped us with:

  • Being able to get an instant full featured API on top of the database.
  • Reduce communication to the backend team to create an endpoint for each niche scenario.
  • Save contextual data for that apps purpose without unnecessarily bogging down the main database.
  • Add several role types and permissions using the Hasura GUI.
  • Call related data in a single query.

Modern app with aggregated data sources

After discovering the remote schemas feature it became a good fit for a new project based around coastal safety that involved aggregating data from multiple sources from google APIs to CMS driven APIs. Some of the data sources were not in digital form but rather knowledge from experience around currents, tides cut offs etc and needed to be collected using a content management system for example, experienced RNLI lifeguards pointing out hazards such as dangerous rocks and shipwrecks by tapping a location on a map and writing about the hazard to warn beach goers. Another CMS would be used for collecting information about general beach information and local amenities, as most things are location based this could be combined with the google distance matrix API to show real world distances and travel times.

Connecting all of this on the front end would have been tedious to say the least as there are a few different formats that come back depending on the API, but by using remote schemas this could all be normalised and abstracted from the end application leaving a single consistent API.

Wrapping up

We believe Hasura offers a powerful solution for modernising and streamlining your backend infrastructure. It provides an instant API layer over your databases, and drastically reduces the need for dedicated backend development. Its remote schema feature greatly simplifies dealing with complex and/or varying data, and managing multiple authentication mechanisms. A solution like Hasura enables you to focus on delivering customer focused experiences without backend bottlenecks and delivery risks.

Sounds good right? If you're experiencing similar issues with data fragmentation, disparate backends, or delivery bottlenecks get in touch with us. We'd love to explore how we can help you and identify the right solution for your challenge.