DEV Community

Jamie Barton for Hygraph

Posted on • Updated on • Originally published at notrab.dev

Create your own Apollo Data Source package

Apollo data sources are a great way to interface with your data(base) in Apollo Server.

The Apollo docs will teach you a lot more about using Apollo data sources, and all their configuration. The purpose of this tutorial is to encourage you to think of ways you can use data sources to improve your workflow.

I have recently started to explore the Give Food API. It's a REST API that aims to index all of the UK food banks, covering networked and independent organisations.

I started working on a few projects using the REST API, but I wanted a way to interface with Give Food using GraphQL.

Since Give Food don't have a native GraphQL API, I decided to explore creating an Apollo Server that masked requests to Give Food using the apollo-datasource-rest module.

However, there is a 2nd GraphQL API which needs access to Give Food API. Instead of creating the GiveFoodDataSource class again in that repo, I decided to package this up into an NPM module that can be imported and used directly with Apollo Server.

npm i apollo-datasource-givefood
Enter fullscreen mode Exit fullscreen mode

Apollo Server accepts a dataSources function that expects your data sources.

import { GiveFoodDataSource } from 'apollo-datasource-givefood';

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () => ({
    givefood: new GiveFoodDataSource(),
  }),
});
Enter fullscreen mode Exit fullscreen mode

These data sources are then accessible inside your GraphQL resolver context.

Query: {
  foodbank: async (_source, { slug }, { dataSources }) => {
    return dataSources.givefood.getBySlug(slug);
  },
}
Enter fullscreen mode Exit fullscreen mode

What's involved?

The GiveFoodDataSource class extends the RESTDataSource and has defined the methods for retrieving data from the Give Food REST API.

Give Food exposes an API for all organisations, food bank by slug, and search params for lat/lng or address.

It made sense to split all of this out into 4 methods:

  • getAll()
  • getBySlug(slug)
  • getByLatLng(lat, lng)
  • getByAddress(address)

For each of these methods, we can use the class methods to get data from our baseURL. baseURL is required by RESTDataSource and all requests to get/post, etc. are relative to this.

In the end, the code ended up being:


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

class GiveFoodDataSource extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'https://www.givefood.org.uk/api/1/';
  }

  async getAll() {
    return this.get('foodbanks');
  }

  async getBySlug(slug) {
    return this.get(`foodbank/${slug}`);
  }

  async getByLatLng(lat, lng) {
    return this.get(`foodbanks/search`, {
      lattlong: `${lat},${lng}`,
    });
  }

  async getByAddress(address) {
    return this.get(`foodbanks/search`, {
      address,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

You can even go further by subclassing the GiveFoodDataSource if Give Food updated their API baseURL, or you wanted to add a method.

import { GiveFoodDataSource } from 'apollo-datasource-givefood';

class FoodBanks extends GiveFoodDataSource {
  constructor() {
    super();
    this.baseURL = '...';
  }

  getFoodBankBySlug(slug) {
    return this.getBySlug(slug);
  }
}
Enter fullscreen mode Exit fullscreen mode

I hope this is some help and inspiration to start building your own data sources. As you can see, this package isn't doing much but providing a clear and declarative way to call the Give Food API.

This is now available inside my GraphQL context. πŸš€

Links

Top comments (2)

Collapse
 
dudeawesome profile image
Dude Awesome

apollo-datasource-rest really lacks for head requests and reading the header of the REST response. Cannot understand why this is missing

Collapse
 
notrab profile image
Jamie Barton

It’s open source project, maybe this is something for hacktoberfest?