@nic-jennings/sql-datasource
Version:
SQL DataSource with Batching and Caching Support
82 lines (81 loc) • 2.93 kB
JavaScript
import { createHash } from "crypto";
import DataLoader from "dataloader";
import knex from "knex";
import knexTinyLogger from "knex-tiny-logger";
import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
const { DEBUG } = process.env;
let hasCache = false;
let hasLogger = false;
let hasBatch = false;
export class BatchedSQLDataSource {
cache;
context;
db;
constructor(config) {
this.cache = config.cache ? config.cache : new InMemoryLRUCache();
this.context = config.context;
const { queryConnection, writeConnection } = this._connectToDatabase(config);
this.db = {
query: queryConnection,
write: writeConnection,
};
this._extendKnex();
}
_extendKnex() {
const _this = this;
const knexQueryBuilder = knex.QueryBuilder;
if (!this.db.query.cache && !hasCache) {
knexQueryBuilder.extend("cache", function (ttl) {
return _this._cacheQuery(this, ttl);
});
hasCache = true;
}
if (!this.db.query.batch && !hasBatch) {
knexQueryBuilder.extend("batch", function (callback) {
const query = this.clone();
return _this._batchQuery(query, callback);
});
hasBatch = true;
}
}
_connectToDatabase(config) {
let seperateInstances = true;
const queryConnection = typeof config.knexConfig === "function"
? config.knexConfig
: knex(config.knexConfig);
if (!config.writeKnexConfig) {
seperateInstances = false;
config.writeKnexConfig = queryConnection;
}
const writeConnection = typeof config.writeKnexConfig === "function"
? config.writeKnexConfig
: knex(config.writeKnexConfig);
if (DEBUG && !hasLogger) {
hasLogger = true; // Prevent duplicate loggers
knexTinyLogger(queryConnection); // Add a logging utility for debugging
if (seperateInstances)
knexTinyLogger(writeConnection); // Add a logging utility for debugging
}
return { queryConnection, writeConnection };
}
_batchQuery(query, callback) {
return new DataLoader((keys) => {
const finalQuery = query.clone();
return callback(finalQuery, keys);
});
}
_cacheQuery(query, ttl = 5) {
const cacheKey = createHash("sha1")
.update(query.toString())
.digest("base64");
return this.cache?.get(cacheKey).then((entry) => {
if (entry)
return Promise.resolve(JSON.parse(entry));
return query.then((rows) => {
if (rows)
this.cache?.set(cacheKey, JSON.stringify(rows), { ttl });
return Promise.resolve(rows);
});
});
}
}