Engineering

Easier GraphQL wrappers for your REST API’s

No items found.

A very common use case for building GraphQL API’s is to wrap REST API’s for a better experience in the Frontend and also some more type checking in our backend.

Apollo knows this so there is a package to help with this and it’s called apollo-datasource-rest. It’s strong points is that it handles some of the caching for you as it runs through their cache and also it exposes a class that makes it easier for us to keep our code clean.

If you wanna follow along you can start with the starter ApolloServer sandbox on CodeSandbox that you can find here.

Wrap our REST API

For this example I am going to use the RandomUser API and we will create two queries. One for getting one random user and one for getting a set of users.

So let’s start by defining our Schema and it should look something like:

const typeDefs = gql`
  type Name {
    title: String,
    first: String,
    last: String,
  }
  type Location {
    street: String
    city: String
    state: String
    postcode: String
  }
  type Picture {
    large: String
    medium: String
    thumbnail: String
  }
  type User {
    gender: String
    name: Name
    location: Location
    email: String
    phone: String
    cell: String
    picture: Picture
    nat: String
  }
  type Query {
    getUser: User
    getUsers(people: Int): [User]
  }
`;

Some of the return values from the API I don’t really want to return so I will ignore them.

const { RESTDataSource } = require("apollo-datasource-rest");

class RandomUser extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = "https://randomuser.me/api/";
  }
}

module.exports = RandomUser;

Now let’s create our class where we will use apollo-datasource-rest and in here the first thing we need to do is to extend the RESTDataSource class that the package exposes to us.

In it we have a constructor where we can define our base API url. Our file should look something like:

async getThing(id) {
  return this.get(`things/${id}`);
}

Now that we have the basis of our class we need to define our functions and they will all have a signature close to this:

async getThing(id) {
  return this.get(`things/${id}`);
}

So we create async functions that will fetch our data, give it any parameters that we want and from the parent RESTDataSource we have access to some methods to interact with API’s.

Besides the get method we also have the normal methods to allow for POST, PUT, PATCH, and DELETE requests so you can build any type of request to your REST api.

Let’s create our function to get one single user:

const { RESTDataSource } = require("apollo-datasource-rest");

class RandomUser extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = "https://randomuser.me/api";
  }

  async getUser() {
    const user = await this.get("/");

    return user.results[0];
  }
}

module.exports = RandomUser;

Now that we have this we can use it in our resolvers.

Using our data sources

Moving back to our index.js when we initialise our ApolloServer there is an option we can pass called dataSources and that goes after our resolvers definition.

This property takes a function that will return an object with all your dataSources so in our case our new ApolloServer instantiation will now look like:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () => ({
    RandomUser: new RandomUser()
  })
});

By passing it as a dataSource we now have access to it on all our resolvers as part of the third argument so in our resolver to get the user we need to call the function we created in our class like so:

const resolvers = {
  Query: {
    getUser: async (_, __, { dataSources }) => dataSources.RandomUser.getUser()
  }
};

If you now try to run the getUser query like so:

{
	getUser {
    name {
      first
      last
    }
  }
}

You should already see some data

Let’s now code our getUsers function and in this one let’s also allow the user pass the number of users they wants to receive.

The endpoint for that is: https://randomuser.me/api/?results=number so let’s code our function with some sensible defaults:

async getUsers(people = 10) {
  const user = await this.get(`/?results=${people}`);

  return user.results;
}

So here we get a variable called people and assign it a default of 10 so if the user doesn’t pass a the argument we will just send 10 random people. This feature is called Default Parameters and you can read all about it at MDN.

Let’s now add a new function to our resolvers for query:

getUsers: async (_, { people }, { dataSources }) =>
  dataSources.RandomUser.getUsers(people)

In here we use the second parameter of the function and that’s because we get whatever the user passed as a parameter to the query.

If you now run something like:

{
  getUsers(people: 3) {
    name {
      first
      last
    },
    location {
      city
    }
  }
}

You should get three random users.

Wrapping up

In my opinion this is a more elegant way of wrapping our REST API’s as we have a clear definition of what we are doing in each function and also we get Apollo to take care of all that’s not the actual fetching and modelling of our data.

If you want the see the full coded example you can see it here.

Easier GraphQL wrappers for your REST API’s
was originally published in YLD Blog on Medium.
Share this article: