Skip to content

Commit cff075c

Browse files
unicodeveloperpeggyrayzis
authored andcommitted
Add pagination
1 parent 183e705 commit cff075c

File tree

2 files changed

+127
-2
lines changed

2 files changed

+127
-2
lines changed
352 KB
Loading

docs/source/tutorial/resolvers.md

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,136 @@ A response like this should be returned at the right side of the playground.
143143

144144
![All Launches](../images/launches.png)
145145

146+
<h2 id="pagination">Pagination</h2>
147+
148+
The `launches` query returned a large data set of launches. This data set contains too much data to be fetched all at once.
149+
150+
Pagination is a solution to the challenge of fetching all the data at once. It's a technique that ensures that the server only sends data in small chunks per time. There are two major ways of fetching paginated data: offset-based (otherwise known as numbered pages), and cursor-based pagination.
151+
152+
Cursor-based pagination is the more efficient way of fetching paginated data because it eliminates the possibility of skipping items and displaying the same item more than once. In cursor-based pagination, a constant pointer is used to keep track of where in the data set the next items should be fetched from. This pointer is called a **cursor**.
153+
154+
We'll use a cursor-based pagination for our graph API. This approach requires two parameters:
155+
156+
* The number of items to fetch at once.
157+
* The cursor.
158+
159+
Open up the `src/schema.js` file and update the `Query` type. Go ahead and add a new type called `LaunchConnection` to the schema as shown below:
160+
161+
_src/schema.js_
162+
163+
```js
164+
type Query {
165+
launches(
166+
"""
167+
The number of results to show. Must be >= 1. Default = 20
168+
"""
169+
pageSize: Int
170+
"""
171+
If you add a cursor here, it will only return results _after_ this cursor
172+
"""
173+
after: String
174+
): LaunchConnection!
175+
launch(id: ID!): Launch
176+
me: User
177+
}
178+
179+
"""
180+
Simple wrapper around our list of launches that contains a cursor to the
181+
last item in the list. Pass this cursor to the launches query to fetch results
182+
after these.
183+
"""
184+
type LaunchConnection {
185+
cursor: String!
186+
hasMore: Boolean!
187+
launches: [Launch]!
188+
}
189+
...
190+
```
191+
192+
The `launches` field takes in two parameters, `pageSize` and `after`. `pageSize` refers to the number of items to show at once while `after` refers to the cursor that keeps track of where the next set of data should be fetched from. We created a `LaunchConnection` type that returns a result that shows the list of launches with a `cursor` field and a `hasMore` field to indicate if there's more data to be fetched.
193+
194+
Copy the pagination helper code below and paste it in the `src/utils.js` file.
195+
196+
_src/utils.js_
197+
198+
```js
199+
module.exports.paginateResults = ({
200+
after: cursor,
201+
pageSize = 20,
202+
results,
203+
// can pass in a function to calculate an item's cursor
204+
getCursor = () => null,
205+
}) => {
206+
if (pageSize < 1) return [];
207+
if (!cursor) return results.slice(0, pageSize);
208+
209+
const cursorIndex = results.findIndex(item => {
210+
// if an item has a `cursor` on it, use that, otherwise try to generate one
211+
let itemCursor = item.cursor ? item.cursor : getCursor(item);
212+
// if there's still not a cursor, return false by default
213+
return itemCursor ? cursor === itemCursor : false;
214+
});
215+
216+
return cursorIndex >= 0
217+
? cursorIndex === results.length - 1 // don't let us overflow
218+
? []
219+
: results.slice(
220+
cursorIndex + 1,
221+
Math.min(results.length, cursorIndex + 1 + pageSize),
222+
)
223+
: results.slice(0, pageSize);
224+
results.slice(cursorIndex >= 0 ? cursorIndex + 1 : 0, cursorIndex >= 0);
225+
};
226+
```
227+
228+
The code above is a helper function for paginating data from the server.Now, let's update the necessary resolver functions to accomodate pagination.
229+
230+
First, copy the code below and add it to the top of the `src/resolvers.js` file.
231+
232+
```js
233+
const { paginateResults } = require('./utils');
234+
```
235+
236+
We required the `paginateResults` function from the `src/utils.js` file. Now, update the `launches` resolver function in the `src/resolvers.js` file with the code below:
237+
238+
_src/resolvers.js_
239+
240+
```js
241+
...
242+
Query: {
243+
launches: async (root, { pageSize = 20, after }, { dataSources }) => {
244+
const allLaunches = await dataSources.launchAPI.getAllLaunches();
245+
const launches = paginateResults({
246+
after,
247+
pageSize,
248+
results: allLaunches,
249+
});
250+
251+
return {
252+
launches,
253+
cursor: launches.length ? launches[launches.length - 1].cursor : null,
254+
// if the cursor of the end of the paginated results is the same as the
255+
// last item in _all_ results, then there are no more results after this
256+
hasMore: launches.length
257+
? launches[launches.length - 1].cursor !==
258+
allLaunches[allLaunches.length - 1].cursor
259+
: false,
260+
};
261+
},
262+
...
263+
```
264+
265+
Let's test the cursor-based pagination we just implemented. Go ahead and run your GraphQL server, write a `launches` query in GraphQL Playground and pass a `pageSize` value of 3.
266+
267+
The response should look like the paginated data shown below:
268+
269+
![Paginated launches data](../images/paginatedlaunches.png)
270+
146271
<h2 id="authentication">Authenticate users</h2>
147272
148-
Authentication is a common part of every application. There are several ways to handle authentication and authorization.
273+
Authentication is a common part of every application. There are several ways to [handle authentication and authorization in your graph API](https://www.apollographql.com/docs/guides/access-control.html).
149274
150-
In this tutorial, we use a login token in an HTTP authorization header.
275+
In this tutorial, we use a login token in an HTTP authorization header and put user info on the `context`.
151276
152277
Update the `context` section of the `ApolloServer` constructor in `src/index.js` to have the code shown below:
153278

0 commit comments

Comments
 (0)