Implementing pagination in NestJS with TypeORM: A comprehensive guide

Instead of running two separate queries, is there a way to retrieve the total count and records together in a single query?

If combining them is not possible, is there a method to reuse the where condition in both queries?

async findAll(query): Promise<Paginate> {
  const take = query.take || 10
  const skip = query.skip || 0
  const keyword = query.keyword || ''

  const builder = this.userRepository.createQueryBuilder("user")
  const total = await builder.where("user.name like :name", { name: '%' + keyword + '%' }).getCount()
  const data = await builder.where("user.name like :name", { name: '%' + keyword + '%' }).orderBy('name', 'DESC').skip(skip).take(take).getMany();

  return {
    data: data,
    count: total
  }
}

{
  count: 10,
  data: [
    {
      id: 1,
      name: 'David'
    },
    {
      id: 2,
      name: 'Alex'
    }]
}

Answer №1

If you're looking for some great examples, check out this project. One standout feature of typeorm is its handy method called findAndCount, which is perfect for scenarios like this.

async findAll(query): Promise<Paginate> {
    const take = query.take || 10
    const skip = query.skip || 0
    const keyword = query.keyword || ''

    const [result, total] = await this.userRepository.findAndCount(
        {
            where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
            take: take,
            skip: skip
        }
    );

    return {
        data: result,
        count: total
    }
}

You can access the Repository API here. For more information on the Repository class, refer to the documentation here.

Answer №2

in conclusion...

This special software checks for the presence of specified take and skip parameters in the URL. If they are found, it converts them from strings to numbers. If not specified, default values of 10 for take and 0 for skip are used.

The take parameter determines the number of results displayed per page, while skip indicates the starting point for reading records.

By setting up this middleware, I am able to intercept requests made to the "product/paged" route specifically for the GET method.

This allows me to extract these values in the controller and pass them on to TypeORM or an SQL query.

https://i.sstatic.net/fAZAv.png

@Injectable()
export class PagerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    req.query.take = +req.query.take || 10;
    req.query.skip = +req.query.skip || 0;
    next();
  }
}

implementation in module.

export class AdminFeatureApi implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(PagerMiddleware)
    .forRoutes({ path: 'product/paged', method: RequestMethod.GET })
  }
}

Controller

@Controller('product')
export class TrainingDomainController {
  constructor(private service: YourService) {}

  @Get('paged')
  get(@Query() { take, skip }) {
    return this.service.findAll(take, skip);
  }
}

and the service component

@Injectable()
export class YourService {
  constructor(
    @InjectRepository(YourEntity)
    private readonly repo: MongoRepository<YourEntity>
  ) {}

  async findAll(take: number = 10, skip: number = 0) {
    const [data, total] = await this.repo.findAndCount({ take, skip });
    return { data, total };
  }
}

satisfactory?

Answer №3

My preference is to use pages instead of skipping directly

  • Here's an example endpoint: /users?page=4&take=3

    async findAll(query): Promise<Paginate> {
        const take = query.take || 10
        const page=query.page || 1;
        const skip= (page-1) * take ;
        const keyword = query.keyword || ''
    
        const [result, total] = await this.userRepository.findAndCount(
            {
                where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
                take: take,
                skip: skip
            }
        );
    
        return {
            data: result,
            count: total
        }
    }
    

    2/. A better way to handle the response:

     async findAll(query): Promise<Paginate> {
         const take= query.take || 10
         const page=query.page || 1;
         const skip= (page-1) * take ;
         const keyword = query.keyword || ''
    
         const data = await this.userRepository.findAndCount(
             {
                 where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
                 take: take,
                 skip: skip
             }
         );
         return paginateResponse(data ,page,take)
    
     }
    
    export function paginateResponse(data,page,limit) {
      const [result, total]=data;
      const lastPage=Math.ceil(total/limit);
      const nextPage=page+1 >lastPage ? null :page+1;
      const prevPage=page-1 < 1 ? null :page-1;
      return {
        statusCode: 'success',
        data: [...result],
        count: total,
        currentPage: page,
        nextPage: nextPage,
        prevPage: prevPage,
        lastPage: lastPage,
      }
    }
    

Answer №5

There are two ways to achieve this: the first is by using createQueryBuilder and the second is with findAndCount

const userRepository = dataSource.getRepository(User);
const _take = query.take || 10;
const _skip = query.skip || 0;

Using createQueryBuilder

const qb = await dataSource
    .getRepository(User)
    .createQueryBuilder("user")
    .orderBy("user.id", "DESC")
    .take(_take)
    .skip(_skip);

    const users = await qb.getMany();
    const total = await qb.getCount();

Alternatively, you can use findAndCount. This method allows you to retrieve the total results in a single call.

const [users, total] = await userRepository.findAndCount({
        order: {
            id: 'DESC'
        },
        skip: _skip,
        take: _take
});

Answer №6

When dealing with a large number of records that require pagination, such as during a data migration or bulk update process.

async retrievePaginatedData(query: any, transactionManager?: EntityManager): Promise<any> {

}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

how can JavaScript be used to retrieve an object based on a condition from an array of objects and an ArrayList

My JavaScript challenge involves working with an array of objects called arrobj and a list called prgList. The goal is to extract the names from arrobj based on the programs listed in prgList. If the program exists in arrobj, it should be displayed accor ...

Attempting to quiet TS2339 through the utilization of the suppressExcessPropertyErrors compiler setting

Take a look at this code snippet: var x = {}; x.test = 'abc'; An issue arises with the TypeScript compiler showing the error message: TS2339: Property 'test' does not exist on type '{}'. To tackle this warning for object ...

Angular service is able to return an Observable after using the .then method

I am currently facing an issue with retrieving the authentication status in a service method. Everything seems to be working fine except for the return statement. I am struggling with the usage of .then inside .map and I am unable to figure out how to retu ...

What is the best way to retrieve JSON data from a raw.github URL and save it into a variable?

Suppose there is a JSON file named data.json on Github. The raw view of the file can be accessed through a URL like this: https://raw.githubusercontent.com/data.json (Please note that this URL is fictional and not real). Assume that the URL contains JSON ...

Error: AppModule requires an array of arguments in order to function properly

Upon successfully compiling my Angular application and running ng serve, I encountered the following error in the browser console. AppComponent_Host.ngfactory.js? [sm]:1 ERROR Error: Arguments array must have arguments. at injectArgs (core.js:1412) at c ...

What is the reasoning behind triggering ValueChanges during initialization even when there are no changes detected?

I am working with a popup component that includes a mat-datepicker. When the user changes the date, I need to update this value in another control and ensure that the start and end controls are valid. However, due to a bug in the mat-date-range-input, I ...

Failing Cypress component test: When leveraging an index.ts file for importing and exporting services

Tech stack: Angular v15 and Cypress v12. My example component that I'm testing: import { Component } from '@angular/core'; import { UserHttp } from '../../services'; @Component({ selector: 'example-view', templateUr ...

A guide on simulating childprocess.exec in TypeScript

export function executeCommandPromise(file: string, command: string) { return new Promise((resolve, reject) => { exec(command, { cwd: `${file}` }, (error: ExecException | null, stdout: string, stderr: string) => { if (error) { con ...

I possess a dataset and desire to correlate each element to different elements

[ { "clauseId": 1, "clauseName": "cover", "texts": [ { "textId": 1, "text": "hello" } ] }, { "clauseId": 3, "clauseName": "xyz", "te ...

The parameter cannot be assigned to type 'HTMLCanvasElement | null' due to conflicting arguments

I am encountering an issue with the following code, as it fails to compile: import React, {useEffect} from 'react' import {Card, Image} from 'semantic-ui-react' import * as chart from 'chart.js' export const PieChartCard = ...

Encountering problem with '@datadog/browser-rum' compilation related to the 'allowedTracingOrigins' attribute

I'm facing a typing problem with the @datadog/browser-rum library: Error: node_modules/@datadog/browser-rum-core/src/domain/configuration.ts:100:3 error TS2322: Type '{ applicationId: string; version: string; actionNameAttribute: string; premium ...

NESTJS Alert: Unable to identify a GraphQL output type for the specified entity. Ensure that your class has the correct decorator applied to it

I am currently in the process of developing a NestJS API utilizing GraphQL, PostgreSQL, and Docker. However, I encountered an error when attempting to build my API: /app/node_modules/@nestjs/graphql/dist/schema-builder/factories/output-type.factory.js:19 s ...

Updating pointers to elements depending on their current status

I am working with an array and looping through it to create div elements. Depending on whether the divs are 'active' (determined by state), I want to assign them the ref property. The ref is also the parameter for a hook that adds an onclick list ...

Unable to simulate a static method in Vitest - encountering an error stating "is not a function"

I am currently writing unit tests using the Vitest framework for my TypeScript and React application, but I have encountered an issue with mocking static methods. Below is a snippet of my code: export class Person { private age: number; constructor(p ...

When compiling TypeScript code with Google API libraries, the error TS2304 occurs stating "Unable to locate the identifier 'Long'"

After adding "@google-cloud/logging-winston":"2.1.0", to my package.json, I encountered these errors during compilation. This issue seems to be related to Google libraries and might stem from automatically generated types from protobuf definitions. ../n ...

Automatically injecting dependencies in Aurelia using Typescript

Recently, I started working with Typescript and Aurelia framework. Currently, I am facing an issue while trying to implement the @autoinject decorator in a VS2015 ASP.NET MVC 6 project. Below is the code snippet I am using: import {autoinject} from "aure ...

Error found in ngrx/effects with Typescript: the name used for a computed property must be associated with a predefined symbol

Currently diving into ngrx to set up an app state in my ionic 3.9.2 application (relying on this tutorial for guidance: ) Encountering an error while attempting to run the app: typescript: ...foo/bar/node_modules/@ngrx/effects/src/on_run_effects.d.ts, li ...

"What is the best way to determine the data type of an object retrieved from an API in TypeScript

Hey there, I'm currently developing a web application using Angular 2 and I'm focusing on implementing an exception handling mechanism. To achieve this, I've created a model that mirrors the object structure I will receive from the server (E ...

Sharing the input value with a service in Angular 4

I am a beginner when it comes to Angular 4. I currently have a variable named "md_id" which is connected to the view in the following way. HTML: <tr *ngFor="let item of driverData"> <td class="align-ri ...

document.addEventListener versus $().on

Recently, I came across an odd behavior while trying to add event listeners to the document. Strangely, when adding listeners to HTMLElements, everything worked smoothly, but for some reason, adding a listener to the document did not have any effect. Howev ...