DEV Community

Cover image for Generating a REST API from a GraphQL Schema
K for moesif

Posted on • Originally published at moesif.com

Generating a REST API from a GraphQL Schema

Cover image by Alexandra on Flickr

Arguing about technology choices is a huge thing, the RESTFul and GraphQL debate is an example. But sometimes there methods to unite the different ways. Sofa is one of them that tries to united REST and GraphQL.

Why

REST and GraphQL are different approaches to build HTTP based APIs, but the opinions on which is best suited for a problem differ from developer to developer.

I don't know if one of them is superior to the other, but sometimes it can be a dealbreaker if we only provide one type of API to our customers.

Sofa allows us to automatically generate a REST API from a GraphQL schema so that we don't have to implement two APIs on our own.

What

Sofa is a Node.js package that takes a GraphQL schema definition and creates an Express middleware from it. This middleware provides REST-API endpoints.

How

Let's set up a small API server with the help of Sofa, GraphQL, and Express and then try to create and read messages from it.

Implementation

First, we use npm to init a new Node.js project and install the packages.

$ mkdir api-server
$ cd api-server
$ npm init
$ npm i express express-graphql graphql-tools sofa-api
Enter fullscreen mode Exit fullscreen mode

Next, we create a GraphQl schema in a new schema.gql file.

type Message {
  id: ID!
  text: String!
}

type Query {
  message(id: ID!): Message
  messages: [Message!]
}

type Mutation {
  writeMessage(title: String!): Message
}

schema {
  query: Query
  mutation: Mutation
}
Enter fullscreen mode Exit fullscreen mode

We only have a Message type and the corresponding types for queries and mutations.

We also need to define resolvers for each query and mutation. Let's create a new resolver.js file for this.

const messages = [];

module.exports = {
  Query: {
    message: (_, { id }) => messages[id],
    messages: () => messages
  },
  Mutation: {
    writeMessage: (_, { text }) => {
      const message = { id: messages.length, text };
      messages.push(message);
      return message;
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

We use the message array as the data-store and its length to generate IDs for our messages. This will be enough since we don't have a delete mutation that could mess up the IDs.

Now, we wire that schema up with GraphQL and Sofa. We do this in a new index.js file.

const fs = require("fs");
const { makeExecutableSchema } = require("graphql-tools");
const express = require("express");
const graphql = require("express-graphql");
const sofa = require("sofa-api").default;

const typeDefs = fs.readFileSync("./typeDefs.gql", "utf8");
const resolvers = require("./resolvers");

const schema = makeExecutableSchema({ typeDefs, resolvers });

const server = express();

server.use("/graphql", graphql({ schema, graphiql: true }));
server.use("/rest", sofa({ schema }));

server.listen("9999");
Enter fullscreen mode Exit fullscreen mode

Let's go through the critical parts of this file.

First, we read the GraphQL type definition from disk and require the corresponding resolvers.

We then use the graphql-tools to create a schema from the resolvers and the type definition.

After that, we create an Express server and set the middlewares for the two endpoints, /graphql and /rest.

We also enable graphiql, the default GraphQL UI so that we can test the GraphQL endpoint in the browser.

Finally, we start a server on port 9999.

Usage

Let's start the server.

$ node .
Enter fullscreen mode Exit fullscreen mode

And open Graphiql in the browser: localhost:9999/graphql

In the text-area on the left, we can now input Queries and mutations. Let's start with a mutation since our store is empty at the moment.

mutation {
  writeMessage(text: "my messsage") {
    id
    text
  }
}
Enter fullscreen mode Exit fullscreen mode

We can send the message to the server with Ctrl+Enter.

The answer should be as following:

{
  "data": {
    "writeMessage": {
      "id": "0",
      "text": "my messsage"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's do this again with a different text, to see if the IDs get counted up as expected:

mutation {
  writeMessage(text: "another message") {
    id
    text
  }
}
Enter fullscreen mode Exit fullscreen mode

The returned JSON should reflect the new text and id.

Let's try the queries.

First load all messages:

query {
  messages {
    id
    text
  }
}
Enter fullscreen mode Exit fullscreen mode

This should deliver JSON with a list of all of our messages.

Only one query missing: message

This time we need to provide the ID of the message we want to load.

query {
  message(id: 1) {
    id
    text
  }
}
Enter fullscreen mode Exit fullscreen mode

We now know that the GraphQL API works as expected, let's try the REST version.

The mutation works via a POST request; this can be done with cURL.

$ curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"text":"REST message"}' \
  http://localhost:9999/rest/add-message
Enter fullscreen mode Exit fullscreen mode

The queries work with GET requests so that we can use a simple link in the browser.

Listing all messages:

localhost:9999/rest/messages

Getting one message:

localhost:9999/rest/message/2

Conclusion

Sofa is certainly an interesting solution to the REST <-> GraphQL divide. It leverages the fact that GraphQL requires a standardized schema and resolvers to map many concepts back to REST APIs.

This allows API creators to provide their customers with different API types while only having to maintain one code-base.

Moesif is the most advanced API Analytics platform, supporting REST, GraphQL, Web3 JSON-RPC and more. Thousands of API developers process billions of API calls through Moesif for debugging, monitoring and discovering insights. Learn More


Originally published at www.moesif.com.

Oldest comments (0)