Skip to content

Commit 5a42337

Browse files
committed
v0.2.0
closes cvburgess#19
1 parent d58dc9a commit 5a42337

File tree

9 files changed

+637
-36
lines changed

9 files changed

+637
-36
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v0.2.0 - 08-07-2019
4+
5+
- Fix issues with lexical scoping [#19]
6+
- Deprecate this.knex in favor of this.db
7+
- Mark batching as experimental
8+
- Update docs to reflect current development status
9+
310
## v0.1.7 - 24-04-2019
411

512
- Fix for responses from sqlite and mssql drivers [#9]
@@ -33,3 +40,11 @@
3340

3441
- Initial publication
3542
- Adds SQLCache and SQLDataSource classes
43+
44+
- [#2] https://github.com/cvburgess/SQLDataSource/issues/2
45+
- [#3] https://github.com/cvburgess/SQLDataSource/issues/3
46+
- [#5] https://github.com/cvburgess/SQLDataSource/issues/5
47+
- [#12] https://github.com/cvburgess/SQLDataSource/issues/12
48+
- [#15] https://github.com/cvburgess/SQLDataSource/issues/15
49+
- [#9] https://github.com/cvburgess/SQLDataSource/issues/9
50+
- [#19] https://github.com/cvburgess/SQLDataSource/issues/19

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
This package combines the power of [Knex] with the ease of use of [Apollo DataSources].
44

5-
**NOTE: THIS PACKAGE HAS OFFICIAL SUPPORT FOR POSTGRESQL AND SQLITE ONLY**
5+
## BREAKING CHANGE IN v0.2.0
66

7-
Other DBs will work, but they will return the default response from `knex.raw()` which may or may not be what you are expecting. I am working on support for other DBs, but if you want to open a PR, that helps!
7+
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.
8+
9+
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()`.
10+
11+
**If you find a way to implement caching without using `knex.raw()` please open a PR!**
12+
13+
While I would love to spend more time investigating this, unfortunately my time is limited at the moment.
814

915
## Getting Started
1016

@@ -33,7 +39,7 @@ class MyDatabase extends SQLDataSource {
3339
constructor() {
3440
super();
3541
// Add your instance of Knex to the DataSource
36-
this.knex = knex;
42+
this.db = knex;
3743
}
3844

3945
getUsers() {
@@ -43,13 +49,13 @@ class MyDatabase extends SQLDataSource {
4349
// A promise without any caching or batching
4450
return query;
4551

46-
// Batch the query with DataLoader
52+
// Batch the query with DataLoader - RETURNS A RAW RESPONSE
4753
return this.getBatched(query);
4854

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

52-
// Batch the query and cache the result for 1 minute
58+
// Batch the query and cache the result for 1 minute - RETURNS A RAW RESPONSE
5359
return this.getBatchedAndCached(query, MINUTE);
5460
}
5561
}
@@ -85,6 +91,8 @@ This method accepts one parameter:
8591

8692
- `knexQuery`: <knexObject> A knex object that has not been then'd
8793

94+
**NOTE: This method will return the raw response from your DB driver.**
95+
8896
### Caching ( getCached )
8997

9098
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.
@@ -116,6 +124,8 @@ const query = this.db.select().from("users");
116124
return this.getBatchedAndCached(query, MINUTE);
117125
```
118126

127+
**NOTE: This method will return the raw response from your DB driver.**
128+
119129
### initialize
120130

121131
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.

SQLCache.js

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,24 @@ const DataLoader = require("dataloader");
44
class SQLCache {
55
constructor(cache = new InMemoryLRUCache(), knex) {
66
this.cache = cache;
7-
this.knex = knex;
87
this.loader = new DataLoader(rawQueries =>
98
Promise.all(rawQueries.map(rawQuery => knex.raw(rawQuery)))
109
);
1110
}
1211

13-
normalizeDBResult(result) {
14-
switch (this.knex.client) {
15-
case "postgres":
16-
return result && result.rows;
17-
case "mssql":
18-
return result;
19-
case "sqlite3":
20-
return result;
21-
// TODO: Test and implement remaining clients
22-
case "mysql":
23-
case "mysql2":
24-
case "oracledb":
25-
case "redshift":
26-
default:
27-
return result;
28-
}
29-
}
30-
3112
getCacheKeyForQuery(query) {
3213
const queryString = query.toString();
3314
return `sqlcache:${queryString}`;
3415
}
3516

3617
getBatched(query) {
18+
// eslint-disable-next-line
19+
console.warn(
20+
"WARNING: batching is considered unstable and returns RAW responses"
21+
);
22+
3723
const queryString = query.toString();
38-
return this.loader.load(queryString).then(this.normalizeDBResult);
24+
return this.loader.load(queryString);
3925
}
4026

4127
getCached(query, ttl) {

SQLDataSource.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,25 @@ const { DEBUG } = process.env;
66

77
let hasLogger = false;
88

9+
const configError = Error("You must set this.db to a valid knex instance");
10+
911
class SQLDataSource extends DataSource {
1012
initialize(config) {
1113
this.context = config.context;
12-
this.db = this.knex;
14+
this.db = this.db || this.knex;
15+
16+
if (!this.db) throw configError;
17+
if (this.knex) {
18+
// eslint-disable-next-line
19+
console.warn("this.knex has been deprecated, use this.db instead");
20+
}
1321

1422
if (DEBUG && !hasLogger) {
1523
hasLogger = true; // Prevent duplicate loggers
1624
knexTinyLogger(this.db); // Add a logging utility for debugging
1725
}
1826

19-
this.sqlCache = new SQLCache(config.cache, this.knex);
27+
this.sqlCache = new SQLCache(config.cache, this.db);
2028
this.getBatched = query => this.sqlCache.getBatched(query);
2129
this.getCached = (query, ttl) => this.sqlCache.getCached(query, ttl);
2230
this.getBatchedAndCached = (query, ttl) =>

SQLDataSource.test.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
const Knex = require("knex");
2+
const SQLDataSource = require("./SQLDataSource");
3+
const mockConsole = require("jest-mock-console").default;
4+
5+
const { TEST_PG_URL } = process.env;
6+
7+
// Create and destroy connection pool for tests
8+
let knex;
9+
10+
beforeAll(() => {
11+
knex = Knex({ client: "pg", connection: TEST_PG_URL });
12+
});
13+
14+
afterAll(() => {
15+
return knex.destroy();
16+
});
17+
18+
describe("Configuration", () => {
19+
test("an error is thrown if this.db is not set", () => {
20+
class TestDB extends SQLDataSource {
21+
getFruit() {
22+
const query = this.db
23+
.select("*")
24+
.from("fruit")
25+
.where({ id: 1 });
26+
return this.getCached(query, 1);
27+
}
28+
}
29+
30+
const testDB = new TestDB();
31+
const initialize = () => testDB.initialize({});
32+
33+
expect(initialize).toThrow(Error);
34+
});
35+
36+
test("an warning to be logged if batching is used", () => {
37+
class TestDB extends SQLDataSource {
38+
constructor() {
39+
super();
40+
this.db = knex;
41+
}
42+
43+
getFruit() {
44+
const query = this.db
45+
.select("*")
46+
.from("fruit")
47+
.where({ id: 1 });
48+
return this.getBatchedAndCached(query, 1);
49+
}
50+
}
51+
52+
const testDB = new TestDB();
53+
testDB.initialize({});
54+
55+
const restoreConsole = mockConsole();
56+
return testDB.getFruit().then(() => {
57+
expect(console.warn).toHaveBeenCalled(); // eslint-disable-line
58+
restoreConsole();
59+
});
60+
});
61+
});
62+
63+
describe("PostgreSQL", () => {
64+
test("Batching and caching is configured", () => {
65+
class TestDB extends SQLDataSource {
66+
constructor() {
67+
super();
68+
this.db = knex;
69+
}
70+
71+
getFruit() {
72+
const query = this.db
73+
.select("*")
74+
.from("fruit")
75+
.where({ id: 1 });
76+
return this.getCached(query, 1);
77+
}
78+
}
79+
80+
const testDB = new TestDB();
81+
testDB.initialize({});
82+
83+
return testDB.getFruit().then(result => {
84+
expect(result).toMatchSnapshot();
85+
});
86+
});
87+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`PostgreSQL Batching and caching is configured 1`] = `
4+
Array [
5+
Object {
6+
"id": 1,
7+
"name": "apple",
8+
},
9+
]
10+
`;

index.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// NOTE: Never use .toBe() for testing values use .toEqual()
2+
// https://jestjs.io/docs/en/expect#toequalvalue
3+
4+
describe("jest", () => {
5+
test("is configured", () => expect(true).toBeTruthy());
6+
});

0 commit comments

Comments
 (0)