Cursor-based pagination is an efficient method for navigating through large datasets. Unlike traditional offset pagination, cursor-based pagination uses a pointer to fetch records after or before it, providing a faster and more scalable solution. In this tutorial, we’ll explore how to implement cursor-based pagination in NestJS with two practical examples.
Setup Your NestJS Project
First, ensure you have NestJS CLI installed. If not, you can install it with:
npm i -g @nestjs/cli
Then, create a new NestJS project:
nest new pagination-example
After the project setup, navigate to your project directory:
cd pagination-example
Example 1: Basic Cursor-Based Pagination
In our first example, we’ll implement a simple cursor-based pagination feature for a fictional “posts” resource. Let’s start by creating a Post module, controller, and service.
nest generate module posts
nest generate controller posts
nest generate service posts
In the posts.service.ts
, we’ll add a method to simulate fetching posts from a database:
findAll(cursor: string, limit: number): Post[] { /* Simulation of fetching posts with cursor and limit */ }
In posts.controller.ts
, we’ll create a GET endpoint to handle pagination:
@Get()
findAll(@Query('cursor') cursor: string, @Query('limit') limit: number): Post[] { return this.postsService.findAll(cursor, limit); }
With this basic setup, clients can fetch posts by providing a cursor ID and a limit. The cursor ID should be the ID of the last post in their current list.
Example 2: Advanced Cursor-Based Pagination with Real Database
In the second example, let’s integrate a real database. First, install TypeORM and the respective driver of your database, for example for PostgreSQL:
npm install @nestjs/typeorm typeorm pg
Next, configure the TypeORM module in app.module.ts
:
imports: [TypeOrmModule.forRoot({...})]
In our post.entity.ts
, define a Post entity:
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@CreateDateColumn()
createdAt: Date;
}
Modify posts.service.ts
to use the repository for accessing posts:
constructor(@InjectRepository(Post) private postRepository: Repository) {}
async findAll(cursor: string, limit: number): Promise<Post[]> { return this.postRepository.find({ where: { id: MoreThan(cursor) }, order: { id: 'ASC' }, take: limit }); }
This setup ensures that whenever a client requests posts with a cursor, the service fetches posts from the database with IDs greater than the cursor ID, complying with cursor-based pagination principles.
Implementing Cursor Encoding and Decoding
To enhance our cursor pagination, let’s implement cursor encoding and decoding techniques. This practice obfuscates the cursor’s true value, adding a layer of security and complexity.
Install the js-base64
library for easy encoding and decoding:
npm install js-base64
In your service, you can now encode the ID:
const encodedCursor = Base64.encode(cursor.toString());
And decode it when a request is received:
const decodedCursor = Base64.decode(cursor);
This method allows for a more secure way to handle cursors, protecting against potential data inference or manipulation.
Conclusion
Cursor-based pagination provides a swift, scalable way to manage large datasets in NestJS applications. By following the examples provided, developers can implement both basic and advanced cursor pagination systems. With cursor encoding and real-database integration, your pagination can achieve enterprise-level efficiency and security.