Connect a database

In the previous tutorial, we created schemas for our read only RESTAPI. And in this tutorial, we will be connecting our graph API to a database for saving and fetching user data.

Here we will be using SQLite for our SQL database, and Sequelize for our ORM. Don?t worry about installing these packages as our package.json already have them, thus they were installed in the first part of this tutorial with npm install.

Also, since this section contains some SQL-specific code that isn't necessary to understanding Apollo data sources, we've already built a UserAPI data source for you in src/datasources/user.js. Please navigate to that file so we can explain the overall concepts.

Build a custom data source

Apollo doesn't have support for a SQL data source yet, so we will need to create a custom data source for our database by extending the generic Apollo data source class. You can create your own with the apollo-datasource package.

Here are some of the core concepts for creating your own data source:

  • The initialize method: You'll need to implement this method if you want to pass in any configuration options to your class. Here, we're using this method to access our graph API's context.
  • this.context: A graph API's context is an object that's shared among every resolver in a GraphQL request. We're going to explain this in more detail in the next section. Right now, all you need to know is that the context is useful for storing user information.
  • Caching: While the REST data source comes with its own built in cache, the generic data source does not. You can use our cache primitives to build your own, however!

Let's go over some of the methods we created in src/datasources/user.js to fetch and update data in our database. You will want to reference these in the next section:

  • findOrCreateUser({ email }): Finds or creates a user with a given email in the database
  • bookTrips({ launchIds }): Takes an object with an array of launchIds and books them for the logged in user
  • cancelTrip({ launchId }): Takes an object with a launchId and cancels that launch for the logged in user
  • getLaunchIdsByUser(): Returns all booked launches for the logged in user
  • isBookedOnLaunch({ launchId }): Determines whether the logged in user booked a certain launch

Add data sources to Apollo Server

Now that we've built our LaunchAPI data source to connect our REST API and our UserAPI data source to connect our SQL database, we need to add them to our graph API.

Adding our data sources is simple. Just create a dataSources property on your ApolloServer that corresponds to a function that returns an object with your instantiated data sources. Let's see what that looks like by navigating to src/index.js and adding the code below:

const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const { createStore } = require('./utils');
const LaunchAPI = require('./datasources/launch');
const UserAPI = require('./datasources/user');
const store = createStore();
const server = new ApolloServer({
  dataSources: () => ({    launchAPI: new LaunchAPI(),    userAPI: new UserAPI({ store })  })});
server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);

First, we import our createStore function to set up our database, as well as our data sources: LaunchAPI and UserAPI. Then, we create our database by calling createStore. Finally, we add the dataSources function to our ApolloServer to connect LaunchAPI and UserAPI to our graph. We also pass in our database we created to the UserAPI data source.

If you use this.context in your datasource, it's critical to create a new instance in the dataSources function and to not share a single instance. Otherwise, initialize may be called during the execution of asynchronous code for a specific user, and replace the this.context by the context of another user.

Now that we've hooked up our data sources to Apollo Server, it's time to move on to the next section and learn how to call our data sources from within our resolvers

Previous: Hook up your data sources
Next: Write your graph's resolvers