DEV Community

loading...
Cover image for Hasura + GraphQL Mesh = <3

Hasura + GraphQL Mesh = <3

Stephen G. Friend
Mentor, Creator, Web Developer @UnderArmour E-Commerce #veteran #remote (he/him) Nascent πŸ¦• Deno-saur / πŸ¦€ Rustacean πŸ‘‹ hello@stephengfriend.com
・6 min read

The Tweet

Early last week while scrolling through Twitter, I had noticed a new tool announced to the world, it was GraphQL Mesh. Normally I would have just read a few comments and likely moved along. However, I am currently looking at ways for us to incorporate legacy endpoints in our architecture at work. So, I spent a few minutes looking over the documentation and through the codebase. I was blown away by the claims and had to prove to myself that it was too good to be true.

I had already spent a couple of weekends familiarizing myself with Hasura. It seemed like a really great fit for a project I have been building at work for the past 6 months. We have a few GraphQL endpoints that exist within our organization but no sort of federation or "one graph to rule them" yet. We need a light amount of application data to be persisted, but most of our data is gathered from upstream providers; a mix of Protobuf, HTTP/REST-like, and GraphQL.

To date, we have been writing custom data sources that use these endpoints and, where necessary, massage the data to fit into a coherent graph. Much bike-shedding and yak shaving have been performed. Hasura felt almost too perfect of a fit, and while not far from perfect for us, it did have a few gotchas that left me searching for alternatives.

Enter GraphQL Mesh

While remote schemas in Hasura appealed to me, almost immediately, I hadn't noticed this meant remote GraphQL-only schemas. This was helpful for the couple of dispersed endpoints we have, But it didn't help resolve our needs for non-GraphQL endpoints. When I saw the tweet, referenced above, over the weekend, I immediately started digging deep into and trying to use GraphQL Mesh as a gateway to our legacy endpoints.

Caution!
GraphQL Mesh is an exciting new technology. As with all exciting new technologies, it may take time for the API surface to stabilize. I would not consider this to be Production Ready by most organization's standards. Give it a 🌟 star or 🍴 fork the repository on Github, if you find it as interesting as I did.

The rest of this article is a walk-through and demonstration of the integration. If you'd like to see the code, you can find it on GitHub. I will need to build a better demo before it can be played with, but plan on doing that next.

If you'd like, you can also follow me or check out my example repository on GitHub.

Getting Started

Pre-requisites

1) Make a directory and initialize a new npm package.json

$ mkdir hasura-mesh && cd hasura-mesh
$ npm init

2) Add the dependencies we will require

Heads Up
At the time of publishing, an alpha dependency is required to pass our config as an object, rather than read from the filesystem

$ npm install graphql apollo-server-micro @graphql-mesh/runtime@0.0.16-alpha-0383167.40+0383167 @graphql-mesh/openapi @graphql-mesh/graphql

3) Setup the folder structure

$ mkdir api
$ touch api/mesh.js

4) Add code to api/mesh.js

// These are required to get the now cli to bundle them
import "@graphql-mesh/graphql"
import "@graphql-mesh/openapi"

import { getMesh, processConfig } from "@graphql-mesh/runtime";
import { ApolloServer } from "apollo-server-micro";

// In a production environment, we would want to secure this endpoint
export default async (req, res) => {

  // Literal config object replaces .meshrc.yaml for serverless
  const parsedConfig = await processConfig({
    "sources": [
      {
        "name": "Weather",
        "handler": {
          "openapi": {
            "source": "https://api.apis.guru/v2/specs/weatherbit.io/2.0.0/swagger.json"
          }
        }
      },
      {
        "name": "Location",
        "handler": {
          "graphql": {
            "endpoint": "https://api.everbase.co/graphql"
          }
        }
      }
    ]
  });

  const { schema, contextBuilder: context } = await getMesh(parsedConfig);

  return new ApolloServer({
    schema,
    context,
    introspection: true, // Required for Hasura Remote Schema introspection
    playground: true, // Optional: Default is `false` in production
  }).createHandler({ path: "/api/mesh" })(req, res);
};

export const config = {
  api: {
    bodyParser: false
  }
};

5) Deploy to Zeit Now

Heads Up
Zeit now requires a globally unique project name, so moving forward in this guide we will use $projectName in place of the one you choose during deploy. Replace all instances of $projectName with the literal value for your project name.

$ npx now

6) Confirm our mesh gateway is working

Open a browser and navigate to https://$projectName.now.sh/api/mesh and we should see a GraphQL Playground instance where we can test our mesh.

Heads Up
You will need your Weatherbit.io api key for this next step. Add the following object to the Query Variables tab at the bottom: { "apiKey": "your-weatherbit.io-api-key"} replacing the value with your api key.

query GraphQLMesh($apiKey: String!){
  docs: url(url: "https://graphql-mesh.com/docs/getting-started/introduction") {
    scheme
    host
    path
  }
  weather: getCurrentCityCityCountryCountry(city: "Tampa", country: "US", key: $apiKey) {
    tampa: data {
        observedAt: obTime
      stateCode
      temp
      precip
    }
  }
}

Assuming everything is working as expected, you should see something similar to the following screenshot.

Screenshot of GraphQL Playground with validation query and response

Adding Hasura

At this point, we can use our new GraphQL Mesh gateway just like any other Apollo Server. But Hasura has some desirable features such as Events and Real-time subscriptions that would be great to make use of.

Heads Up
At the time of publishing, Hasura is unable to make top-level requests across schemas. This means that querying a top-level field created from the data schema and from a remote schema in the same request is not possible. Follow this issue for updates to this limitation.

Luckily, the Hasura team has made available a very simple and guided Heroku deploy. For the sake of brevity, we will use this method. Just click the below button to start the process.

Deploy

Heads Up
Zeit now requires a globally unique project name, so moving forward in this guide we will use $projectName in place of the one you choose during deploy. Replace all instances of $projectName with the literal value for your project name.

After our service is up and running, head on over to your sweet new Hasura console, located at https://$appName.herokuapp.com. You should see a view similar to the below screenshot.

Hasura Console

Adding data and verifying

In the Hasura Console, navigate to the Remote Schemas tab. Give your remote schema a name, for the url we will want to use https://$projectName.now.sh/api/mesh where $projectName is the name of our Zeit Now project.

Once our remote schema has been added, we can head back to the GraphiQL tab and test our new mesh. We'll use the same query from above to test the integration.

Heads Up
You will need your Weatherbit.io api key for this next step. Add the following object to the Query Variables tab at the bottom: { "apiKey": "your-weatherbit.io-api-key"} replacing the value with your api key.

query GraphQLMesh($apiKey: String!){
  docs: url(url: "https://graphql-mesh.com/docs/getting-started/introduction") {
    scheme
    host
    path
  }
  weather: getCurrentCityCityCountryCountry(city: "Tampa", country: "US", key: $apiKey) {
    tampa: data {
        observedAt: obTime
      stateCode
      temp
      precip
    }
  }
}

Finally, as above, assuming everything was set up correctly we should receive a near-identical response through Hasura that we did via our GraphQL Mesh gateway. Below is an example of what you should see.

Screenshot of GraphiQL Explorer with validation query and response

Congratulations!

At this point, you should have a fully functional Hasura instance on Heroku with a remote schema proxying a GraphQL Mesh gateway running on Zeit Now. Creating tables, events and real-time subscriptions are out of scope, given the time we have today. I plan to learn and share more about GraphQL Mesh in the coming weeks.

I hope you have found value in this short article. If you have found any issues with the above content, I would love to hear from you. Please reach out in the comments or find me on Twitter.

Discussion (7)

Collapse
huaixing profile image
Huaixing

I thought you treated Hasura as an orm like Prisma. The thing is, Hasura lacks some constraints, which makes it too dangerous to put it public. Like data validation, and complex auth rules. I'm always waiting for Hasura Action to make it really production. Otherwise, it's probably only suitable for internal usage. I thought you put Hasura behind GraphQL Mesh. I guess now we have to try Apollo Federation and put it in front of Hasura.

Collapse
stephengfriend profile image
Stephen G. Friend Author • Edited

I think that is a common misconception about Hasura. They mention throughout much of their documentation that running their engine at the edge is the best way to leverage their product as a platform. Many of their proposed benefits are lost once it is stuffed behind another federated endpoint. In fact, their first 3 bullets of their feature callout is in direct conflict with your concerns.

Screenshot of Hasura feature callout with bullets, 'Add to a new or existing database,' 'powerful authorization engine,' and 'can safely be used by frontend apps directly'

While we may be able to argue whether or not they are successfully performing those exact functions as we might generally expect, it is clear they intend this product to be your primary entrypoint for client-based graphs.

Here is a link to their role-based authorization documentation: hasura.io/docs/1.0/graphql/manual/...

Additionally, GraphQL Mesh does not need to be run as it's own gateway. It can be used to purely generate a comprehensive SDK which is then used in whatever fashion is best for your business. One example, which I hope to showcase in the coming days, is how it could be used to quickly create data sources for your federated graph. This is extremely powerful if you have a dispersed set of endpoints with varying communication paradigms (openapi, protobuf, soap, etc...)

Collapse
arnaudjnn profile image
Arnaud Jeannin • Edited

What would be the architecture using GraphQL Mesh as it's own gateway? Would be awesome to use it as a remote schema as described here instead of OneGraph: hasura.io/blog/remote-joins-a-grap...

Thread Thread
stephengfriend profile image
Stephen G. Friend Author

The example included is exactly that. GraphQL Mesh builds the schema which is then made available to your GraphQL server implementation. In this case, I'm using Apollo Server and deploying it to Zeit Now. This endpoint is a bonafide Graph based on GraphQL Mesh. Later in the article, I use it as a remote schema in Hasura. I'm hoping for that article to come to fruition because it is the precise architecture that I'd like to work with.

Collapse
go4cas profile image
Cas du Plessis

@stephenfriend ... very interesting post! I am looking at the combining the same tech at the moment. So, in your design, for this post, Hasura would be the interface to the client apps, right? Then using Hasura remote schemas, you glue all the schemas together? It would be interesting to see the differences if one used graphql-mesh as the client interface, and the Hasura API as one of the back-end apps? Your thoughts?

Collapse
stephengfriend profile image
Stephen G. Friend Author

Correct, in my example, the Hasura instance is the primary endpoint for the client app. The main reason I chose Hasura for the ingress was that their docs specifically mention clients connecting directly to it as a feature and the GraphQL Mesh docs specifically mentioned that it would be better if it was proxied. I had actually considered putting Hasura behind Mesh, after I did this.

Hasura includes authorization and real-time subscriptions that I don't think are possible to capitalize on when it's not the client endpoint. If you're just looking for Postgres-as-a-Graph, I think the @graphql-mesh/postgraphile handler might be the better option.

Collapse
soldierforus profile image
Travis Bennett

A very well written article, I look forward to going doing the walk-through, not to mention perusing your next article!