WWT will be dropping support for Internet Explorer 11 on wwt.com in early June 2021. Please switch to a modern browser for the best experience.
?

Applying a GraphQL Layer on Top of Existing Services With Mesh

Sometimes you want to have the flexibility that GraphQL has to offer but are deterred by the potential overhead/maintainability cost. This is an option that could help streamline conversations around tooling and tradeoffs when it comes to GraphQL.

January 20, 2021 6 minute read

Opinions on GraphQL aside, if you ever come across a decision point around using GraphQL in a project, this is an option. As a part of the decision-making process around determining the right tools for the job, just knowing more options can be paramount.

Initial setup

There are a couple of ways to GraphQL-ize your existing services and they'll depend on specific use cases, needs, and/or wants. Today we'll focus on churning out a simplistic GraphQL gateway and handling a JSON-schema use case.

  1. Run yarn init in an empty folder (I named mine mesh), step through all the prompts, and cd into it.
  2. Run yarn add graphql @graphql-mesh/cli @graphql-mesh/graphql @graphql-mesh/json-schema to install the relevant packages.
  3. Add a .meshrc.yaml file in that same directory (this is the configuration file that graphql-mesh uses in order to determine how to relate data sources to your graph)

And that's it! The rest of the way will depend on what your schema looks like.

Scenario

Before anything else, here is the Github repository if you want something to follow along with. It also has an existing sample API that I leveraged for the graphql-mesh work.

Let's say we have an existing RESTful API out there that has an endpoint /users and you want to be able to consume that through your GraphQL gateway. Let's make use of graphql-mesh!

The first step is understanding what your source looks like. graphql-mesh has support for multiple handlers already, but you can also use custom handlers or extend existing ones to meet your specific needs. We'll be looking at how to apply a JSON-schema handler (hence the @graphql-mesh/json-schema package).

The second step is figuring out what bits and pieces go into your .meshrc.yaml. A majority of it is self-explanatory. Here's what I've got:

sources:
  - name: ProjectBuildoutApi
    handler:
      jsonSchema:
        baseUrl: http://localhost:3000
        operations:
          - type: Query
            field: users
            path: /users
            method: GET
            responseSchema: ./jsonSchemas/usersResponse.json
  • The sources node is a list of your named sources, each with its own handlers. We only have one and it uses a jsonSchema type handler.
  • handler refers to the graphql-mesh's implementation of how to handle specific sources. There are a few default handlers supported already but is open to extension and/or composition (or an entirely new handler for your specific use case)
  • name here refers to what you'd like to name this specific source. I named mine 'ProjectBuildoutApi' since this particular source has more than just users.
  • baseUrl is what it sounds like; it's the jumping-off point for your operations. I currently have it pointing to a running API service on my local that I've set up for this purpose. You'll potentially have more operations here as you add more functionality (maybe more GET 's, or a POST if you're feeling daring).
  • type refers to the GraphQL operation type. We're just making a straightforward query.
  • field refers to the identifier you'd like to use for this data when you make a GraphQL query to this gateway.
  • path refers to the path to get your users, based off of the baseUrl; this includes any string-interpolated values (i.e. if I were to have a /users endpoint that had room for an id path argument, path would be something like /users/{args.id}.
  • responseSchema is the location of the schema for the response that you expect from this particular endpoint.

Defining the responseSchema

In order for this to work, you'll want to define the structure of what to expect when you make a call to the endpoint you want to consume. I made a directory to house my schemas called jsonSchemas and I created a usersResponse.json file in there with these contents:

{
  "type": "object",
  "title": "UsersResponse",
  "description": "A list of Users in ProjectBuildout",
  "properties": {
    "data": {
      "type": "array",
      "items": {
        "type": "object",
        "title": "User",
        "description": "An instance of a User",
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      }
    }
  }
}
  • The title and description properties document what to expect and what the objects are; you'll want it to be informative, as you would for any domain-driven object.
  • type refers to whether the property in the JSON at a particular hierarchy is an object or an array.
  • data is simply the property of the response with a type of array that houses the User objects.
  • items are specific to array types and it determines the structure of the objects to expect in that array at this property.
  • For this API, a user only has an id, name and email property. We only care about name and email for now and are defined here as such. When you query for id, GraphQL will throw an error because as far as GraphQL is concerned, it's an undefined field.

Running the thing

You're set to go! The last step is to run it. Run yarn graphql-mesh serve and an instance of GraphiQL should run on your browser. Here's a sample query to use:

{
	users {
		data {
			name
		}
	}
}

The browser should look like this after running this query:

This is a screenshot. Note that your defined title and descriptions show up on the SCHEMA and DOCS tab on the right.

Congrats! Now you have the flexibility that GraphQL has to offer without sacrificing too much time/effort.

Summary

There are different ways to enable your team to be more flexible when it comes to deciding the tools for your API. If GraphQL is on the table, this is one of the ways (another is to encapsulate this work in an SDK) you can effectively add that layer of flexibility with subjectively minimal costs in dev time/effort and tech debt reconciliation — it always depends, doesn't it?

Resources

Here are a few links to get familiar with what graphql-mesh is as well and what problem it solves i.e. how do we allow consumers to express what they want to express in the data cost-effectively, with the existing architecture: 

Share this