w3resource

Hook up your data sources


In the last tutorial, we constructed our schema, thus in this tutorial, we will be hooking our data sources to our GraphQL API.

Throughout this section, we'll build data sources for a REST API and a SQL database and connect them to Apollo Server. Don't worry if you're not familiar with neither of those technologies, you won't need to understand them deeply in order to follow along in this tutorial.

Connecting to a REST API

First, let's connect the VeeDrive REST API to our graph. To get started, install the apollo-datasource-rest package, by running the following command:

npm install apollo-datasource-rest

This package exposes the RESTDataSource class that is responsible for fetching data from a REST API. To build a data source for a REST API, extend the RESTDataSource class and define this.baseURL.

In our example, the baseURL for our API is https://api.spacexdata.com/v2/. Let's create our VeeLaunchAPI data source by adding the code below to src/datasources/launch.js:

//src/datasources/launch.js
const {RESTDataSource} = require('apollo-datasource-rest');
class VeeLaunchAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'https://api.spacexdata.com/v2/';
  }
}
module.exports = VeeLaunchAPI;

The Apollo RESTDataSource also sets up an in-memory cache that caches responses from our REST resources with no additional setup. We call this partial query caching. What's great about this cache is that you can reuse existing caching logic which your REST API exposes.

Write data fetching methods

The next step is to add methods to the VeeLaunchAPI data source that correspond to the queries our graph API needs to fetch. According to our schema, we'll need a method to get all of the launches. Let's add a getAllLaunches method to our VeeLaunchAPI class now:

src/datasources/launch.js
async getAllLaunches() {
  const response = await this.get('launches');
  return Array.isArray(response)
    ? response.map(launch => this.launchReducer(launch))
    : [];
}

The Apollo REST data sources have some inbuilt helper methods that correspond to HTTP verbs like GET and POST. In the code above, this.get('launches'), makes a GET request to https://api.spacexdata.com/v2/launches and stores the returned launches in the response variable. Then, the getAllLaunches method maps over the launches and transforms the response from our REST endpoint with this.launchReducer. If there are no launches, an empty array is returned.

Now, we need to write our launchReducer method in order to transform our launch data into the shape our schema expects. We recommend this approach in order to decouple your graph API from business logic specific to your REST API. First, let's recall what our Launch type looks like in our schema. You don't have to copy this code:

//src/schema.js
type Launch {
  id: ID!
  site: String
  mission: Mission
  rocket: Rocket
  isBooked: Boolean!
}

Next, let's write a launchReducer function to transform the data into that shape. Copy the following code into your VeeLaunchAPI class:

//src/datasources/launch.js
launchReducer(launch) {
  return {
    id: launch.flight_number || 0,
    cursor: `${launch.launch_date_unix}`,
    site: launch.launch_site && launch.launch_site.site_name,
    mission: {
      name: launch.mission_name,
      missionPatchSmall: launch.links.mission_patch_small,
      missionPatchLarge: launch.links.mission_patch,
    },
    rocket: {
      id: launch.rocket.rocket_id,
      name: launch.rocket.rocket_name,
      type: launch.rocket.rocket_type,
    },
  };
}

With the above changes, we can easily make changes to the launchReducer method while the getAllLaunches method stays lean and concise. The launchReducer method also makes testing the VeeLaunchAPI data source class easier, which we'll cover later.

Next, let's take care of fetching a specific launch by its ID. Let's add two methods, getLaunchById, and getLaunchesByIds to the VeeLaunchAPI class.

//src/datasources/launch.js
async getLaunchById({ launchId }) {
  const response = await this.get('launches', { flight_number: launchId });
  return this.launchReducer(response[0]);
}
getLaunchesByIds({ launchIds }) {
  return Promise.all(
    launchIds.map(launchId => this.getLaunchById({ launchId })),
  );
}

The getLaunchById method takes in a flight number and returns the data for a particular launch, while getLaunchesByIds returns several launches based on their respective launchIds.

Now that we've connected our REST API successfully,in the next tutorial, we will be connecting our API to a database

Previous: Build a schema
Next: Connect a database