Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users pagination #35

Merged
merged 5 commits into from
Oct 5, 2024
Merged

Users pagination #35

merged 5 commits into from
Oct 5, 2024

Conversation

likui628
Copy link
Owner

@likui628 likui628 commented Oct 5, 2024

Summary by CodeRabbit

  • New Features
    • Enhanced user query functionality to include pagination details in the response, such as total users and total pages.
    • Introduced sorting options for user queries, allowing users to specify order and orderBy parameters.
    • Added a robust pagination mechanism for database operations, improving data retrieval efficiency.
  • Bug Fixes
    • Improved the structure of the response from the user query to provide more comprehensive data.

Copy link

coderabbitai bot commented Oct 5, 2024

Walkthrough

The changes in this pull request enhance the queryUsers function within the src/services/user.ts file by integrating a new paginate utility to manage pagination. This replaces the previous manual handling of pagination parameters. The return structure of queryUsers has been updated to include additional fields: users, page, pageSize, total, and totalPages. The src/utils/prisma.ts file has also been modified to introduce types and a paginate function that supports these enhancements. The src/types/index.ts and src/validations/user.ts files have been updated to reflect changes in pagination options and validation schema.

Changes

File Change Summary
src/services/user.ts Updated queryUsers to utilize the new paginate utility, changing the return type to include pagination details.
src/utils/prisma.ts Introduced types for model names and pagination options, and added the paginate function for handling pagination logic.
src/types/index.ts Modified Pagination interface: changed orderBy to a general string type and renamed limit to pageSize.
src/validations/user.ts Added orderBy and order properties to getUsersSchema.query for enhanced query options.

Possibly related PRs

  • getUsers #26: The getUsers function in src/controllers/user.ts calls the queryUsers method from the src/services/user.ts, which has been enhanced in the main PR to return additional pagination information.
  • Create user #27: The Create user PR modifies the src/controllers/user.ts file, which includes the getUsers function that relies on the queryUsers method, thus connecting it to the changes made in the main PR.
  • updateUser #30: The updateUser PR modifies the src/controllers/user.ts file, where the getUsers function is defined, linking it to the changes in the main PR regarding user querying functionalities.

Poem

In the burrow where users play,
A query now counts the way.
With pages and limits, oh what a sight,
Pagination's a hop, a jump, a delight!
So gather 'round, let’s cheer and say,
"Hooray for the changes that brighten our day!" 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
src/services/user.ts (1)

82-87: LGTM! Consider improving totalPages calculation.

The changes to include pagination information are good. They provide necessary data for implementing pagination on the client side.

However, consider improving the totalPages calculation to handle the case when there are no users:

- const totalPages = Math.ceil(total / limit)
+ const totalPages = Math.max(1, Math.ceil(total / limit))

This ensures that totalPages is always at least 1, which can be more intuitive for consumers of this function.

Also applies to: 94-100

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 85d91f5 and 4850ea6.

📒 Files selected for processing (1)
  • src/services/user.ts (1 hunks)
🔇 Additional comments (2)
src/services/user.ts (2)

Line range hint 1-100: Overall assessment: Good implementation of pagination.

The changes to the queryUsers function effectively implement pagination support. The addition of total count, total pages, and other pagination metadata will greatly improve the usability of this function for client-side pagination.

A few minor improvements have been suggested, mainly around edge case handling and ensuring backwards compatibility. Once these are addressed, the implementation will be robust and ready for use.

Remember to update any consumers of this function to handle the new return type, as it has changed significantly from returning just an array of users to an object containing pagination information.


94-100: Reminder: Update consumers of queryUsers function.

The return type of queryUsers has changed significantly. It now returns an object with pagination information instead of just an array of users.

Please ensure that all consumers of this function are updated to handle the new return type. Run the following script to find potential usage of queryUsers:

Review the results and update the consumers accordingly.

src/services/user.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
src/services/user.ts (1)

86-86: Consider adding a safeguard against division by zero

While the current implementation is robust, it's worth adding a small safeguard to prevent potential division by zero in the totalPages calculation. This could happen if limit is accidentally set to 0.

Consider updating the line as follows:

- const totalPages = Math.max(1, Math.ceil(total / limit))
+ const totalPages = Math.max(1, Math.ceil(total / Math.max(1, limit)))

This ensures that even if limit is 0, the function will behave as if the limit is 1, preventing any potential errors.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4850ea6 and 1bc0059.

📒 Files selected for processing (1)
  • src/services/user.ts (1 hunks)
🔇 Additional comments (3)
src/services/user.ts (3)

82-101: Great implementation of pagination logic!

The changes effectively address the previous comment about handling cases where page exceeds totalPages. The implementation is correct and efficient, providing a robust pagination solution. Here's a breakdown of the improvements:

  1. Calculation of total users and pages (lines 82-87)
  2. Introduction of adjustedPage to handle edge cases (line 87)
  3. Using adjustedPage in the skip calculation (line 93)
  4. Returning comprehensive pagination details (lines 95-101)

These changes significantly enhance the queryUsers function, making it more versatile and user-friendly.


82-101: Summary of changes to queryUsers function

The modifications to the queryUsers function significantly improve its functionality by introducing robust pagination. Key improvements include:

  1. Calculation of total users and pages
  2. Handling of edge cases where requested page exceeds total pages
  3. Returning comprehensive pagination details

These changes enhance the function's versatility and user-friendliness. The implementation addresses previous comments and provides a solid foundation for paginated user queries.

Remember to verify the usage of this function across the codebase due to the change in return type, and consider the minor optimization suggested for the totalPages calculation.

Overall, these changes represent a substantial improvement to the user service.


95-101: Verify usage of queryUsers function across the codebase

The return type of the queryUsers function has been significantly changed. It now returns an object with pagination details instead of just an array of users. This change, while beneficial, may impact other parts of the codebase that use this function.

Please ensure that all callers of the queryUsers function are updated to handle the new return type. You can use the following script to identify potential usage sites:

This will help identify areas that may need updates to accommodate the new return structure.

✅ Verification successful

Usage of queryUsers function verified

Only one usage of the queryUsers function was found in src/controllers/user.ts. Please ensure that this usage correctly handles the new return type with pagination details.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find potential usage of queryUsers function

# Search for queryUsers function calls
echo "Searching for queryUsers function calls:"
rg --type typescript "queryUsers\(" -A 5

# Search for destructuring of users that might need updating
echo "\nSearching for potential destructuring of users that might need updating:"
rg --type typescript "const\s+{\s*users\s*}\s*=\s*await\s*queryUsers" -A 5

Length of output: 542


Script:

#!/bin/bash
# Description: Find potential usage of queryUsers function with corrected file type and regex.

# Search for queryUsers function calls
echo "Searching for queryUsers function calls:"
rg --type ts "queryUsers\(" -A 5

# Search for destructuring of users that might need updating
echo "\nSearching for potential destructuring of users that might need updating:"
rg --type ts 'const\s+{\s*users\s*}\s*=\s*await\s*queryUsers' -A 5

Length of output: 839


Script:

#!/bin/bash
# Description: Find potential usage of queryUsers function with corrected regex patterns.

# Search for queryUsers function calls
echo "Searching for queryUsers function calls:"
rg --type ts "queryUsers\(" -A 5

# Search for destructuring of users that might need updating
echo "\nSearching for potential destructuring of users that might need updating:"
rg --type ts 'const\s+\{\s*users\s*\}\s*=\s*await\s*queryUsers' -A 5

Length of output: 703

@likui628
Copy link
Owner Author

likui628 commented Oct 5, 2024

@coderabbitai
encapsulate the paginate logic

Copy link

coderabbitai bot commented Oct 5, 2024

Tip

For best results, initiate chat on the files or code changes.

@likui628: Understood. Encapsulating the pagination logic will improve code reusability and readability. Here's how you can do it:

Create a pagination utility function:

// src/utils/pagination.ts
export function paginate(page: number, limit: number, total: number) {
  const totalPages = Math.ceil(total / limit) || 1;
  const adjustedPage = Math.min(Math.max(1, page), totalPages);
  const skip = (adjustedPage - 1) * limit;

  return {
    page: adjustedPage,
    limit,
    total,
    totalPages,
    skip,
  };
}

Update the queryUsers function to use the pagination utility:

// src/services/user.ts
import { paginate } from '../utils/pagination';

export async function queryUsers(page: number, limit: number) {
  const total = await prisma.user.count();

  const { page: adjustedPage, limit: adjustedLimit, skip, totalPages } = paginate(page, limit, total);

  const users = await prisma.user.findMany({
    skip,
    take: adjustedLimit,
    orderBy: { id: 'asc' },
  });

  return {
    users,
    page: adjustedPage,
    limit: adjustedLimit,
    total,
    totalPages,
  };
}

This way, the pagination logic is encapsulated in a separate utility function, which can be reused across different services whenever pagination is needed.


✏️ Learnings added
Learnt from: likui628
URL: https://github.com/likui628/node-ts-express-starter/pull/35

Timestamp: 2024-10-05T03:19:33.560Z
Learning: Encapsulate pagination logic into separate utility functions for better code reusability and readability.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (1)
src/utils/prisma.ts (1)

36-39: Consider performance implications of counting large datasets

Using db.count({ where }) can introduce performance overhead on large datasets because it requires scanning and counting a potentially vast number of records. This can impact the responsiveness of your application.

Suggestions:

  • Implement approximate counting: If exact counts are not critical, consider using database-specific features to get an estimated count more efficiently.
  • Introduce caching: Cache count results for frequent queries to reduce database load.
  • Limit maximum pageSize: Enforce a reasonable maximum on pageSize to prevent users from requesting excessively large pages.

These strategies can help mitigate performance issues and improve the scalability of your application.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1bc0059 and 86af865.

📒 Files selected for processing (2)
  • src/services/user.ts (2 hunks)
  • src/utils/prisma.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/user.ts

src/utils/prisma.ts Show resolved Hide resolved
src/utils/prisma.ts Show resolved Hide resolved
src/utils/prisma.ts Outdated Show resolved Hide resolved
src/utils/prisma.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
src/utils/prisma.ts (1)

36-60: Excellent pagination logic and return value

The implementation of the paginate function is well done:

  1. The use of Promise.all for concurrent database operations is efficient.
  2. The totalPages calculation now correctly handles the case when totalCount is zero, addressing a previous review comment.
  3. The return object provides all necessary information for pagination.

These improvements enhance the function's performance and correctness.

Consider adding a comment explaining the totalPages calculation for future maintainers:

// Calculate total pages, ensuring it's 0 when there are no items
const totalPages = totalCount > 0 ? Math.ceil(totalCount / pageSize) : 0
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 86af865 and 48ef9fb.

📒 Files selected for processing (2)
  • src/services/user.ts (2 hunks)
  • src/utils/prisma.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/user.ts
🔇 Additional comments (3)
src/utils/prisma.ts (3)

1-3: LGTM: Import and prisma client initialization

The addition of Prisma to the import statement is appropriate for the new type definitions. The prisma client initialization remains correct.


5-12: ⚠️ Potential issue

Consider using Prisma's public types instead of internal ones

While the type definitions are well-structured, using Prisma.TypeMap accesses Prisma's internal types, which may change in future versions and lead to potential issues. Consider using Prisma's public types for better stability and maintainability.

As suggested in a previous review, you could refactor these types to use the model delegate directly:

type PaginationOptions<ModelDelegate> = {
  model: ModelDelegate;
  where?: Parameters<ModelDelegate['findMany']>[0]['where'];
  orderBy?: Parameters<ModelDelegate['findMany']>[0]['orderBy'];
  include?: Parameters<ModelDelegate['findMany']>[0]['include'];
  page?: number;
  pageSize?: number;
};

This approach leverages Prisma Client's existing types and avoids relying on internal structures.


22-34: ⚠️ Potential issue

Good input validation, but remove @ts-expect-error

The input validation for page and pageSize is a great addition, ensuring they are positive integers. This addresses a previous review comment and improves the function's robustness.

However, the use of @ts-expect-error is concerning as it suppresses a TypeScript error instead of addressing the underlying issue.

As suggested in a previous review, consider refactoring the function to accept the model delegate directly:

export async function paginate<ModelDelegate extends { findMany: Function; count: Function }>({
  model,
  page = 1,
  pageSize = 10,
  where,
  orderBy,
  include,
}: PaginationOptions<ModelDelegate>) {
  // Rest of your code...
}

This approach eliminates the need for @ts-expect-error and provides better type safety.

Comment on lines +13 to +20
type PaginationOptions<ModelName extends ModelNames> = {
modelName: ModelName
where?: PrismaFindManyArgs<ModelName>['where']
orderBy?: PrismaFindManyArgs<ModelName>['orderBy']
include?: PrismaFindManyArgs<ModelName>['include']
page?: number
pageSize?: number
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Well-structured PaginationOptions type, but reconsider use of internal Prisma types

The PaginationOptions type is well-defined and includes all necessary fields for pagination. However, it relies on the previously defined types that use Prisma's internal types.

Consider refactoring this type along with the previous ones to use Prisma's public types, as suggested in the previous comment. This will make your code more resilient to future Prisma updates.

src/utils/prisma.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
src/types/index.ts (1)

Line range hint 17-19: Consider making token optional in UserData interface

The UserData interface extends the User type and adds a token field. However, not all user data might include a token. Consider making the token field optional for more flexibility:

export interface UserData extends User {
  token?: string
}

This change would allow the UserData interface to be used in contexts where a token might not be present, improving its reusability.

src/validations/user.ts (1)

10-13: LGTM! Consider adding a default sorting option.

The addition of orderBy and order properties to the getUsersSchema is a good enhancement. It allows for flexible sorting of user queries, which aligns well with the pagination feature.

Consider setting default values for these properties to ensure consistent behavior when they're not explicitly provided. For example:

orderBy: z
  .enum(['name', 'email', 'role', 'createdAt', 'updatedAt'])
  .default('createdAt')
  .optional(),
order: z.enum(['asc', 'desc']).default('desc').optional(),

This would default to sorting by 'createdAt' in descending order if not specified.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 48ef9fb and 3d1f45d.

📒 Files selected for processing (3)
  • src/services/user.ts (2 hunks)
  • src/types/index.ts (1 hunks)
  • src/validations/user.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/services/user.ts

@@ -1,9 +1,9 @@
import { Role, User } from '@prisma/client'

export interface Pagination {
orderBy?: 'name' | 'email' | 'createdAt' | 'updatedAt'
orderBy?: string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using a union type for orderBy instead of string

While changing orderBy to string provides more flexibility, it reduces type safety. This could potentially lead to runtime errors if invalid values are used. Consider using a union type with a catch-all string literal type for future additions:

orderBy?: 'name' | 'email' | 'createdAt' | 'updatedAt' | (string & {})

This approach maintains some type safety while allowing for future additions.

order?: 'asc' | 'desc'
limit?: number
pageSize?: number
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Incomplete Rename of limit to pageSize in Pagination Context

  • limit is still used in src/validations/common.ts within the pagination validation schema and should be renamed to pageSize to ensure consistency.
  • The usage of limit in src/middlewares/rate-limiter.ts is appropriate and unrelated to pagination.
🔗 Analysis chain

Rename from limit to pageSize looks good

The rename from limit to pageSize is a good change as it's more descriptive and aligns better with pagination terminology. However, ensure that this change is consistently applied throughout the codebase to maintain coherence.

Let's verify the impact of this rename:


Further Verification Needed

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining uses of 'limit' in pagination context
# and ensure 'pageSize' is used consistently

# Search for 'limit' usage in TypeScript files
echo "Searching for 'limit' usage:"
rg --type typescript '\blimit\b' -C 2

# Search for 'pageSize' usage in TypeScript files
echo "Searching for 'pageSize' usage:"
rg --type typescript '\bpageSize\b' -C 2

Length of output: 304


Script:

#!/bin/bash
# Description: Check for any remaining uses of 'limit' in pagination context
# and ensure 'pageSize' is used consistently

# Search for 'limit' usage in TypeScript files
echo "Searching for 'limit' usage:"
rg --glob '*.ts' --glob '*.tsx' '\blimit\b' -C 2

# Search for 'pageSize' usage in TypeScript files
echo "Searching for 'pageSize' usage:"
rg --glob '*.ts' --glob '*.tsx' '\bpageSize\b' -C 2

Length of output: 2739

@likui628 likui628 merged commit 0e14022 into main Oct 5, 2024
5 checks passed
@likui628 likui628 deleted the paginate branch October 5, 2024 09:08
@likui628 likui628 restored the paginate branch October 5, 2024 09:09
@likui628 likui628 deleted the paginate branch October 5, 2024 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant