Skip to content

Commit bd388a8

Browse files
royderksRoy Derks
andauthored
Add example for cursor based pagination (#45)
* Add example for cursor based pagination * Add link in main readme * Moved to different directory, renamed example and simplified * Update package-lock.json --------- Co-authored-by: Roy Derks <[email protected]>
1 parent 5839df6 commit bd388a8

File tree

9 files changed

+781
-225
lines changed

9 files changed

+781
-225
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ These are available in `/sequence`:
8383
- `/transformsInMaterializer` shows how connecting two subgraphs can invoke transformations in between. For example, the `city` of a customer can be fed into a `weather` that takes `lat,lon` by calling some geocoding in the sequence
8484
- `/useOfJSON` shows how, in long sequences, a new type does not have to created for every step--treat everything as JSON up to the last step, and your type system stays clean.
8585

86+
## @dbquery
87+
88+
View the documentation for the [`@dbquery` custom directive](https://www.ibm.com/docs/en/api-connect/ace/saas?topic=directives-directive-dbquery) in the documentation.
89+
90+
Uses a database filled with mock data.
91+
92+
- `/pagination` shows how to implement connection model pagination for a database.
93+
8694
## protection
8795

8896
For more information on protecting your API, [see our documentation](https://stepzen.com/docs/access-control).

dbquery/pagination/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Pagination with `@dbquery`
2+
3+
This snippet shows how `@dbquery` fields are configured to support pagination.
4+
5+
## Getting Started
6+
7+
[Install](https://www.ibm.com/docs/en/api-connect/ace/saas?topic=setting-up-your-environment) the StepZen command line interface.
8+
9+
To run these examples,
10+
11+
- `git clone` this repo
12+
- Change to the right working directory.
13+
- run `stepzen deploy`
14+
15+
This example uses a demo database filled with mock data, you can inspect the `config.yaml` file to find the credentials for this database.
16+
17+
18+
## GraphQL pagination
19+
20+
StepZen supports pagination through the standard [GraphQL Cursor Connection Specification](https://relay.dev/graphql/connections.htm).
21+
22+
This allows clients consuming a StepZen GraphQL endpoint to page through resultsmregardless of the originating backend type (e.g. GraphQL endpoint, database, REST API).
23+
24+
For the `@dbquery` directive, StepZen supports a connection based model for pagination out of the box.
25+
26+
This snippet demonstrates the typical `@dbquery` response and directive configuration for pagination.
27+
28+
In StepZen, Query fields that return a connection type must have two field arguments,
29+
30+
- `first` which specifies the number of nodes to fetch
31+
- `after` which is an opaque cursor.
32+
33+
## Using paginated fields
34+
35+
The easiest way to use paginated fields is with a GraphQL operation that has variables for the arguments `first` and `after`. Examples
36+
are show in `requests.graphql`.
37+
38+
To retrieve the first page of a result, the operation is called with `after` omitted or set to the empty string (`""`), and `first` is set to the number of results to return in a page.
39+
40+
Subsequent pages can then be retrieved by setting `after` to the value of `endCursor` from `pageInfo` from the previous request. `first` should be set to the value used in the first request.
41+
42+
This is continued until `hasNextPage` is `false`.

dbquery/pagination/api.graphql

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# This example shows how @dbquery is configured for pagination.
2+
#
3+
# @dbquery supports connection based pagination out of the box by
4+
# mapping database responses to a consistent standard
5+
# GraphQL pagination approach, specifically
6+
# GraphQL Cursor Connections Specification
7+
# https://relay.dev/graphql/connections.htm
8+
#
9+
#
10+
11+
"""
12+
Sample customer type.
13+
"""
14+
type Customer {
15+
id: ID!
16+
name: String
17+
email: String
18+
}
19+
20+
"""
21+
`CustomerConnection` is the connection type for `Customer` pagination.
22+
In StepZen, a field returning a connection type must have arguments
23+
`first` (number of nodes to fetch) and `after` (opaque cursor indicating
24+
the starting point).
25+
"""
26+
type CustomerConnection {
27+
edges: [CustomerEdge]
28+
pageInfo: PageInfo!
29+
}
30+
31+
"""
32+
`CustomerEdge` provides access to the node and its cursor.
33+
"""
34+
type CustomerEdge {
35+
node: Customer
36+
cursor: String
37+
}
38+
39+
type Query {
40+
"""
41+
`customers` pages through mock `Customer` objects in a demo PostgreSQL database.
42+
The data is exposed using standard GraphQL pagination using the connection model.
43+
"""
44+
customers(first: Int! = 10, after: String! = ""): CustomerConnection
45+
@dbquery(
46+
type: "postgresql"
47+
table: "customer"
48+
schema: "public"
49+
configuration: "postgresql_config"
50+
)
51+
}

dbquery/pagination/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
configurationset:
2+
- configuration:
3+
name: postgresql_config
4+
uri: postgresql://postgresql.introspection.stepzen.net/introspection?user=testUserIntrospection&password=HurricaneStartingSample1934

dbquery/pagination/index.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
schema @sdl(files: ["api.graphql"]) {
2+
query: Query
3+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Page through customers from a database connection based pagination
2+
query CustomersConnectionBased($first: Int!, $after: String = "") {
3+
customers(first: $first, after: $after) {
4+
edges {
5+
node {
6+
id
7+
name
8+
}
9+
}
10+
pageInfo {
11+
endCursor
12+
hasNextPage
13+
}
14+
}
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"endpoint": "api/miscellaneous"
3+
}

dbquery/pagination/tests/Test.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
const fs = require("fs");
2+
const path = require("node:path");
3+
const {
4+
deployAndRun,
5+
authTypes,
6+
getTestDescription,
7+
} = require("../../../tests/gqltest.js");
8+
9+
testDescription = getTestDescription("snippets", __dirname);
10+
11+
const requestsFile = path.join(path.dirname(__dirname), "requests.graphql");
12+
const requests = fs.readFileSync(requestsFile, "utf8").toString();
13+
14+
describe(testDescription, function () {
15+
const CURSOR = "eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjoxfQ=="
16+
17+
const tests = [
18+
{
19+
label: "first set of results",
20+
query: requests,
21+
operationName: "CustomersConnectionBased",
22+
variables: {
23+
first: 2
24+
},
25+
expected: {
26+
customers: {
27+
edges: [
28+
{
29+
node: {
30+
id: "1",
31+
name: "Lucas Bill "
32+
}
33+
},
34+
{
35+
node: {
36+
id: "2",
37+
name: "Mandy Jones "
38+
}
39+
}
40+
],
41+
pageInfo: {
42+
hasNextPage: true,
43+
endCursor: CURSOR
44+
}
45+
}
46+
},
47+
authType: authTypes.adminKey,
48+
},
49+
{
50+
label: "next set of results",
51+
query: requests,
52+
operationName: "CustomersConnectionBased",
53+
variables: {
54+
first: 2,
55+
after: CURSOR
56+
},
57+
expected: {
58+
customers: {
59+
edges: [
60+
{
61+
node: {
62+
id: "3",
63+
name: "Salim Ali "
64+
}
65+
},
66+
{
67+
node: {
68+
id: "4",
69+
name: "Jane Xiu "
70+
}
71+
}
72+
],
73+
pageInfo: {
74+
endCursor: "eyJjIjoiTDpRdWVyeTpjdXN0b21lcnMiLCJvIjozfQ==",
75+
hasNextPage: true
76+
}
77+
}
78+
},
79+
authType: authTypes.adminKey,
80+
},
81+
];
82+
return deployAndRun(__dirname, tests);
83+
});

0 commit comments

Comments
 (0)