/SQLDataSource

SQL DataSource for Apollo GraphQL projects

Primary LanguageJavaScriptMIT LicenseMIT

SQLDataSource

This package combines the power of Knex with the ease of use of Apollo DataSources.

BREAKING CHANGE IN v0.2.0

Batching of queries is hacked together with knex.raw() - while this is not ideal, due to the way Knex works, I have not found a more consistent way to do this.

As such, when you use getBatched or getBatchedAndCached the result will not be the pretty, normalized output you may expect from Knex, it will be the raw output from your DB driver of choice as if you had run the query with knex.raw().

If you find a way to implement caching without using knex.raw() please open a PR!

While I would love to spend more time investigating this, unfortunately my time is limited at the moment.

Getting Started

Installation

To install SQLDataSource: npm i datasource-sql

And the peer dependencies (if you do not have them already): npm i knex graphql

Usage

const Knex = require("knex");
const { SQLDataSource } = require("datasource-sql");

const MINUTE = 60;

const knex = Knex({
  client: "pg",
  connection: {
    /* CONNECTION INFO */
  }
});

class MyDatabase extends SQLDataSource {
  constructor() {
    super();
    // Add your instance of Knex to the DataSource
    this.db = knex;
  }

  getUsers() {
    // This can be any valid Knex query
    const query = this.db.select().from("users");

    // A promise without any caching or batching
    return query;

    // Batch the query with DataLoader - RETURNS A RAW RESPONSE
    return this.getBatched(query);

    // Cache the result for 1 minute
    return this.getCached(query, MINUTE);

    // Batch the query and cache the result for 1 minute - RETURNS A RAW RESPONSE
    return this.getBatchedAndCached(query, MINUTE);
  }
}

And use it in your Apollo server configuration:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  cache,
  context,
  dataSources: () => ({
    db: new MyDatabase()
  })
});

SQLCache

SQLCache is what makes SQLDataSource incredibly smart and preferment. A thin wrapper around Apollo's caching strategy and the optional addition of DataLoader make for the smallest number of queries possible hitting your database.

Batching ( getBatched )

If you were to make the same query (example: knex.select().from('users')) in multiple resolvers you could be making a bunch of duplicate reads from your database.

SQLCache uses DataLoader to create a batched wrapper for knex ( getBatched ) that will only fire duplicate queries once, but returns the same result to all resolvers.

This method accepts one parameter:

getBatched(knexQuery)

  • knexQuery: A knex object that has not been then'd

NOTE: This method will return the raw response from your DB driver.

Caching ( getCached )

If you were to make the same query over the course of multiple requests to your server you could also be making needless requests to your server - especially for expensive queries.

SQLCache leverages Apollo's caching strategy to save results between requests and makes that available via getCached.

This method accepts two parameters:

getCached(knexQuery, ttlInSeconds)

  • knexQuery: A knex object that has not been then'd
  • ttlInSeconds: number of seconds to keep cached results

Why not both?

To leverage caching and batching for a query, use the method getCachedAndBatched which wraps both methods.

This method accepts the same two params as getCached:

getBatchedAndCached(knexQuery, ttlInSeconds)

  • knexQuery: A knex object that has not been then'd
  • ttlInSeconds: number of seconds to keep cached results

From the example in the usage section above:

const query = this.db.select().from("users");
return this.getBatchedAndCached(query, MINUTE);

NOTE: This method will return the raw response from your DB driver.

initialize

SQLDataSource creates a new SQLCache on every initialize call with the cache and context from Apollo Server so you never should be directly creating a new SQLCache.

Note: If no cache is configured, an InMemoryLRUCache cache is used.

SQLDataSource

SQLDataSource is an ES6 Class that can be extended to make a new SQLDataSource and extends Apollo's base DataSource class under the hood.

( See the Usage section above for an example )

Like all DataSources, SQLDataSource has an initialize method that Apollo will call when a new request is routed.

The initialize call accepts a configuration object with a cache and context.

If no cache is provided in your Apollo configuration, SQLDataSource falls back to an InMemoryLRUCache like the RESTDataSource.

context

The context from your Apollo server is available as this.context.

db

The instance of knex you reference in the constructor is made available as this.db.

Query methods

The methods from SQLCache ( getBatched, getCached, and getBatchedAndCached ) are combined with the provided knex object and appended to this.

Debug mode

To enable more enhanced logging via knex-tiny-logger, set DEBUG=true in your environment variables.

Contributing

All contributions are welcome!

Please open an issue or pull request.