diff --git a/src/decorator.spec.ts b/src/decorator.spec.ts index e84ba056..c4cf083b 100644 --- a/src/decorator.spec.ts +++ b/src/decorator.spec.ts @@ -1,5 +1,5 @@ import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants' -import { HttpArgumentsHost, CustomParamFactory, ExecutionContext } from '@nestjs/common/interfaces' +import { CustomParamFactory, ExecutionContext, HttpArgumentsHost } from '@nestjs/common/interfaces' import { Request as ExpressRequest } from 'express' import { FastifyRequest } from 'fastify' import { Paginate, PaginateQuery } from './decorator' @@ -18,6 +18,7 @@ const decoratorfactory = getParamDecoratorFactory(Paginate) function expressContextFactory(query: ExpressRequest['query']): Partial { const mockContext: Partial = { + getType: () => 'http' as ContextType, switchToHttp: (): HttpArgumentsHost => Object({ getRequest: (): Partial => @@ -34,6 +35,7 @@ function expressContextFactory(query: ExpressRequest['query']): Partial { const mockContext: Partial = { + getType: () => 'http' as ContextType, switchToHttp: (): HttpArgumentsHost => Object({ getRequest: (): Partial => diff --git a/src/decorator.ts b/src/decorator.ts index 6ad3d7b6..43241e80 100644 --- a/src/decorator.ts +++ b/src/decorator.ts @@ -1,7 +1,7 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common' import type { Request as ExpressRequest } from 'express' import type { FastifyRequest } from 'fastify' -import { pickBy, Dictionary, isString, mapKeys } from 'lodash' +import { Dictionary, isString, mapKeys, pickBy } from 'lodash' function isRecord(data: unknown): data is Record { return data !== null && typeof data === 'object' && !Array.isArray(data) @@ -50,18 +50,34 @@ function parseParam(queryParam: unknown, parserLogic: (param: string, res: an } export const Paginate = createParamDecorator((_data: unknown, ctx: ExecutionContext): PaginateQuery => { - const request: ExpressRequest | FastifyRequest = ctx.switchToHttp().getRequest() - const query = request.query as Record + let path: string + let query: Record - // Determine if Express or Fastify to rebuild the original url and reduce down to protocol, host and base url - let originalUrl: string - if (isExpressRequest(request)) { - originalUrl = request.protocol + '://' + request.get('host') + request.originalUrl - } else { - originalUrl = request.protocol + '://' + request.hostname + request.url + switch (ctx.getType()) { + case 'http': + const request: ExpressRequest | FastifyRequest = ctx.switchToHttp().getRequest() + query = request.query as Record + + // Determine if Express or Fastify to rebuild the original url and reduce down to protocol, host and base url + let originalUrl: string + if (isExpressRequest(request)) { + originalUrl = request.protocol + '://' + request.get('host') + request.originalUrl + } else { + originalUrl = request.protocol + '://' + request.hostname + request.url + } + + const urlParts = new URL(originalUrl) + path = urlParts.protocol + '//' + urlParts.host + urlParts.pathname + break + case 'ws': + query = ctx.switchToWs().getData() + path = null + break + case 'rpc': + query = ctx.switchToRpc().getData() + path = null + break } - const urlParts = new URL(originalUrl) - const path = urlParts.protocol + '//' + urlParts.host + urlParts.pathname const searchBy = parseParam(query.searchBy, singleSplit) const sortBy = parseParam<[string, string]>(query.sortBy, multipleSplit) diff --git a/src/paginate.ts b/src/paginate.ts index c6ccd64d..1307db5b 100644 --- a/src/paginate.ts +++ b/src/paginate.ts @@ -395,16 +395,6 @@ export async function paginate( items = await queryBuilder.getMany() } - let path: string - const { queryOrigin, queryPath } = getQueryUrlComponents(query.path) - if (config.relativePath) { - path = queryPath - } else if (config.origin) { - path = config.origin + queryPath - } else { - path = queryOrigin + queryPath - } - const sortByQuery = sortBy.map((order) => `&sortBy=${order.join(':')}`).join('') const searchQuery = query.search ? `&search=${query.search}` : '' @@ -429,6 +419,18 @@ export async function paginate( const options = `&limit=${limit}${sortByQuery}${searchQuery}${searchByQuery}${selectQuery}${filterQuery}` + let path: string = null + if (query.path !== null) { + // `query.path` does not exist in RPC/WS requests and is set to null then. + const { queryOrigin, queryPath } = getQueryUrlComponents(query.path) + if (config.relativePath) { + path = queryPath + } else if (config.origin) { + path = config.origin + queryPath + } else { + path = queryOrigin + queryPath + } + } const buildLink = (p: number): string => path + '?page=' + p + options const totalPages = isPaginated ? Math.ceil(totalItems / limit) : 1 @@ -446,13 +448,17 @@ export async function paginate( select: isQuerySelected ? selectParams : undefined, filter: query.filter, }, - links: { - first: page == 1 ? undefined : buildLink(1), - previous: page - 1 < 1 ? undefined : buildLink(page - 1), - current: buildLink(page), - next: page + 1 > totalPages ? undefined : buildLink(page + 1), - last: page == totalPages || !totalItems ? undefined : buildLink(totalPages), - }, + // If there is no `path`, don't build links. + links: + path !== null + ? { + first: page == 1 ? undefined : buildLink(1), + previous: page - 1 < 1 ? undefined : buildLink(page - 1), + current: buildLink(page), + next: page + 1 > totalPages ? undefined : buildLink(page + 1), + last: page == totalPages || !totalItems ? undefined : buildLink(totalPages), + } + : ({} as Paginated['links']), } return Object.assign(new Paginated(), results)