Skip to content

Commit 0b8d76e

Browse files
committed
Move sortFields into setup
1 parent eadb268 commit 0b8d76e

File tree

3 files changed

+54
-38
lines changed

3 files changed

+54
-38
lines changed

README.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Are you running a service, using an SQL database, and want to support cursor sty
44

55
## How it works
66

7-
1. When a request comes in you call the library with a `query` object containing how many items to fetch (`first`/`last`), where to fetch from (`before`/`after`) and the sort config (`sortFields`), along with a `setup` object.
7+
1. When a request comes in you call the library with a `query` object containing how many items to fetch (`first`/`last`), where to fetch from (`before`/`after`), along with a `setup` object which contains the sort config.
88
2. The `runQuery` function you provided in `setup` is invoked, and provided with a `limit`, `whereFragmentBuilder` and `orderByFragmentBuilder`. You integrate these into your query, run it, and then return the results.
99
3. The library takes the results, and for each one it generates a unique `cursor`, which it then returns alongside each row. It also returns `hasNextPage`/`hasPreviousPage`/`startCursor`/`endCursor` properties.
1010

@@ -66,17 +66,17 @@ async function fetchUsers(userInput: {
6666

6767
const { edges, pageInfo } = await withPagination({
6868
query: {
69-
sortFields: [
70-
{ field: 'first_name', order: userInput.order },
71-
{ field: 'last_name', order: userInput.order },
72-
{ field: 'id', order: userInput.order },
73-
],
7469
first: userInput.first,
7570
last: userInput.last,
7671
before: userInput.before,
7772
after: userInput.after,
7873
},
7974
setup: {
75+
sortFields: [
76+
{ field: 'first_name', order: userInput.order },
77+
{ field: 'last_name', order: userInput.order },
78+
{ field: 'id', order: userInput.order },
79+
],
8080
// generate one with `npx -p sql-cursor-pagination generate-secret`
8181
cursorSecret: buildCursorSecret('somethingSecret'),
8282
queryName: 'users',
@@ -139,23 +139,23 @@ E.g.
139139

140140
### Query
141141

142-
| Property | Type | Required | Description |
143-
| ------------ | --------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
144-
| `first` | `number` | If `last` isn't present. | The number of rows to fetch from the start of the window. |
145-
| `last` | `number` | If `first` isn't present. | The number of rows to fetch from the end of the window. |
146-
| `sortFields` | `{ field: string, order: 'asc' \| 'desc' }[]` | Yes | This takes an array of objects which have `field` and `order` properties. There must be at least one entry and you must include an entry that maps to a unique key, otherwise it's possible for there to be cursor collisions, which will result in an exception. |
147-
| `after` | `string` | No | The window will cover the row after the provided cursor, and later rows. This takes the string `cursor` from a previous result`. |
148-
| `before` | `string` | No | The window will cover the row before the provided cursor, and earlier rows. This takes the string `cursor` from a previous result. |
142+
| Property | Type | Required | Description |
143+
| -------- | -------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
144+
| `first` | `number` | If `last` isn't present. | The number of rows to fetch from the start of the window. |
145+
| `last` | `number` | If `first` isn't present. | The number of rows to fetch from the end of the window. |
146+
| `after` | `string` | No | The window will cover the row after the provided cursor, and later rows. This takes the string `cursor` from a previous result`. |
147+
| `before` | `string` | No | The window will cover the row before the provided cursor, and earlier rows. This takes the string `cursor` from a previous result. |
149148

150149
### Setup
151150

152-
| Property | Type | Required | Description |
153-
| ----------------------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
154-
| `runQuery` | `function` | Yes | This function is responsible for running the database query, and returning the array of rows. It is provided with a `QueryContent` object which contains a `WHERE` fragment, `ORDER BY` fragment and `limit`, which must be included in the query. |
155-
| `queryName` | `string` | Yes | A name for this query. It should be unique to the query, and is used to bind the cursors to it. This prevents a cursor that was created for another query being used for this one. |
156-
| `cursorSecret` | `CursorSecret` | Yes | The secret that is used to encrypt the cursor, created from `buildCursorSecret(secret: string)`. Must be at least 30 characters. Generate one with `npx -p sql-cursor-pagination generate-secret`. |
157-
| `maxNodes` | `number` | No | The maximum number of allowed rows in the response before the `ErrTooManyNodes` error is thrown. _Default: 100_ |
158-
| `cursorGenerationConcurrency` | `number` | No | The maximum number of cursors to generate in parallel. _Default: 10_ |
151+
| Property | Type | Required | Description |
152+
| ----------------------------- | --------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
153+
| `runQuery` | `function` | Yes | This function is responsible for running the database query, and returning the array of rows. It is provided with a `QueryContent` object which contains a `WHERE` fragment, `ORDER BY` fragment and `limit`, which must be included in the query. |
154+
| `queryName` | `string` | Yes | A name for this query. It should be unique to the query, and is used to bind the cursors to it. This prevents a cursor that was created for another query being used for this one. |
155+
| `sortFields` | `{ field: string, order: 'asc' \| 'desc' }[]` | Yes | This takes an array of objects which have `field` and `order` properties. There must be at least one entry and you must include an entry that maps to a unique key, otherwise it's possible for there to be cursor collisions, which will result in an exception. |
156+
| `cursorSecret` | `CursorSecret` | Yes | The secret that is used to encrypt the cursor, created from `buildCursorSecret(secret: string)`. Must be at least 30 characters. Generate one with `npx -p sql-cursor-pagination generate-secret`. |
157+
| `maxNodes` | `number` | No | The maximum number of allowed rows in the response before the `ErrTooManyNodes` error is thrown. _Default: 100_ |
158+
| `cursorGenerationConcurrency` | `number` | No | The maximum number of cursors to generate in parallel. _Default: 10_ |
159159

160160
## Query Fragments
161161

src/sql-cursor-pagination.test.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,19 +175,17 @@ describe('SqlCursorPagination', () => {
175175
setup?: Partial<WithPaginationInputSetup<Row, TGenerateCursor>>;
176176
}): Promise<WithPaginationResult<Row, TGenerateCursor>> {
177177
const input: WithPaginationInput<Row, boolean> = {
178-
query: {
179-
sortFields: [
180-
{ field: 'first_name', order: Asc },
181-
{ field: 'last_name', order: Desc },
182-
{ field: 'id', order: Asc },
183-
],
184-
...query,
185-
},
178+
query,
186179
setup: {
187180
cursorSecret: mockCursorSecret,
188181
maxNodes: Infinity,
189182
queryName: mockQueryName,
190183
runQuery: buildRunQuery(),
184+
sortFields: [
185+
{ field: 'first_name', order: Asc },
186+
{ field: 'last_name', order: Desc },
187+
{ field: 'id', order: Asc },
188+
],
191189
...setup,
192190
},
193191
};
@@ -592,6 +590,8 @@ describe('SqlCursorPagination', () => {
592590
const res = await go({
593591
query: {
594592
first: 1,
593+
},
594+
setup: {
595595
sortFields: [{ field: 'users.id', order: Asc }],
596596
},
597597
});
@@ -605,6 +605,8 @@ describe('SqlCursorPagination', () => {
605605
const res = await go({
606606
query: {
607607
first: 1,
608+
},
609+
setup: {
608610
sortFields: [
609611
{ field: { alias: 'email_alias', name: 'email' }, order: Asc },
610612
],
@@ -630,6 +632,8 @@ describe('SqlCursorPagination', () => {
630632
query: {
631633
after: all.edges[0].cursor,
632634
first: 1,
635+
},
636+
setup: {
633637
sortFields: [{ field: 'email', order: Asc }],
634638
},
635639
}),
@@ -649,6 +653,8 @@ describe('SqlCursorPagination', () => {
649653
query: {
650654
before: all.edges[0].cursor,
651655
last: 1,
656+
},
657+
setup: {
652658
sortFields: [
653659
{ field: 'email', order: Asc },
654660
{ field: 'last_name', order: Asc },
@@ -845,6 +851,8 @@ describe('SqlCursorPagination', () => {
845851
await go({
846852
query: {
847853
first: 1,
854+
},
855+
setup: {
848856
sortFields: [],
849857
},
850858
}),
@@ -855,6 +863,8 @@ describe('SqlCursorPagination', () => {
855863
await go({
856864
query: {
857865
first: 1,
866+
},
867+
setup: {
858868
sortFields: [{ field: '!', order: Asc }],
859869
},
860870
}),
@@ -865,6 +875,8 @@ describe('SqlCursorPagination', () => {
865875
await go({
866876
query: {
867877
first: 1,
878+
},
879+
setup: {
868880
// @ts-expect-error invalid order
869881
sortFields: [{ field: 'a', order: 'oops' }],
870882
},
@@ -878,6 +890,8 @@ describe('SqlCursorPagination', () => {
878890
await go({
879891
query: {
880892
first: 1,
893+
},
894+
setup: {
881895
sortFields: [
882896
{ field: 'first_name', order: 'asc' },
883897
{ field: 'first_name', order: 'asc' },
@@ -893,6 +907,8 @@ describe('SqlCursorPagination', () => {
893907
await go({
894908
query: {
895909
first: Infinity,
910+
},
911+
setup: {
896912
sortFields: [{ field: 'first_name', order: Asc }],
897913
},
898914
}),

src/sql-cursor-pagination.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@ export type WithPaginationInputQuery<TGenerateCursor extends boolean = true> = {
5050
first?: number | null;
5151
/* The number of rows to fetch from the end of the window. */
5252
last?: number | null;
53-
/**
54-
* This takes an array of objects which have `field` and `order` properties.
55-
*
56-
* The order can be `asc` or `desc`.
57-
*
58-
* There must be at least one entry and you must include an entry that maps to a unique key,
59-
* otherwise it's possible for there to be cursor collisions, which will result in an exception.
60-
*/
61-
sortFields: readonly FieldWithOrder[];
6253
/**
6354
* The window will cover the row after the provided cursor, and later rows.
6455
*
@@ -92,6 +83,15 @@ export type WithPaginationInputSetup<
9283
* which must be included in the query.
9384
*/
9485
runQuery: (queryContent: QueryContent) => Promise<readonly TNode[]>;
86+
/**
87+
* This takes an array of objects which have `field` and `order` properties.
88+
*
89+
* The order can be `asc` or `desc`.
90+
*
91+
* There must be at least one entry and you must include an entry that maps to a unique key,
92+
* otherwise it's possible for there to be cursor collisions, which will result in an exception.
93+
*/
94+
sortFields: readonly FieldWithOrder[];
9595
/**
9696
* A name for this query.
9797
*
@@ -203,13 +203,13 @@ export async function withPagination<
203203
last = null,
204204
before: beforeInput = null,
205205
after: afterInput = null,
206-
sortFields: _sortFields,
207206
},
208207
setup: {
209208
cursorGenerationConcurrency: _cursorGenerationConcurrency = 10,
210209
cursorSecret = null,
211210
maxNodes = 100,
212211
runQuery,
212+
sortFields: _sortFields,
213213
queryName: _queryName,
214214
},
215215
}: WithPaginationInput<TNode, boolean>): Promise<

0 commit comments

Comments
 (0)