Skip to content

feat: add pagination middleware for resume routes (#515)#642

Merged
anurag3407 merged 1 commit into
anurag3407:mainfrom
Prashansure:feat/pagination-helper
May 18, 2026
Merged

feat: add pagination middleware for resume routes (#515)#642
anurag3407 merged 1 commit into
anurag3407:mainfrom
Prashansure:feat/pagination-helper

Conversation

@Prashansure
Copy link
Copy Markdown
Contributor

@Prashansure Prashansure commented May 18, 2026

Summary

Closes #515

Implements a reusable pagination middleware for the CareerPilot backend.

Changes Made

  • Added backend/src/middleware/paginate.js — generic pagination middleware that extracts page and limit from query params and attaches skip, limit, and page to req.pagination
  • Updated backend/src/routes/resume.js to use the pagination middleware on the resume listing route
  • Set up MongoDB Atlas cluster and configured MONGODB_URI in .env

How to Test

  1. Start the server with npm run dev
  2. Hit GET /api/resumes?page=1&limit=10 with a valid auth token
  3. Response will contain paginated resume results

Type of Change

  • New feature (non-breaking)

Summary by CodeRabbit

  • New Features

    • Resume listings now support pagination and sorting, enabling better navigation through larger collections.
  • Chores

    • Updated environment configuration example for local development setup.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR introduces shared pagination infrastructure for list endpoints. A new pagination middleware parses and validates query parameters, while a response helper formats paginated results. The resume endpoint is updated to use these utilities, returning paginated data with metadata instead of a fixed payload. The environment configuration is updated with local development values.

Changes

Pagination Infrastructure and Resume Integration

Layer / File(s) Summary
Pagination middleware and response helper
backend/src/middleware/paginate.js
paginate() middleware validates and defaults page, limit, and sort from query, computes skip, and attaches config to req.paginate. paginatedResponse() computes pagination metadata and sends a consistent JSON structure with success, data, and meta fields including totalPages, hasNextPage, and hasPrevPage.
Resume endpoint pagination integration
backend/src/routes/resume.js
Resume GET / endpoint now imports and uses pagination utilities. The handler calls Resume.countDocuments() for the total, queries with sort/skip/limit from req.paginate, and returns results via paginatedResponse() instead of a manual response object.
Development environment configuration
backend/.env.example
Adds LINKEDIN_REDIRECT_URI and GEMINI_API_KEY local development values to the example environment file.

Sequence Diagram

sequenceDiagram
  participant Client
  participant paginate as paginate<br/>middleware
  participant Handler
  participant Resume as Resume<br/>model
  participant paginatedResponse
  Client->>paginate: GET /?page=1&limit=10&sort=-createdAt
  paginate->>paginate: Parse, validate, compute skip
  paginate->>Handler: Call next() with req.paginate attached
  Handler->>Resume: countDocuments({ userId })
  Resume-->>Handler: total
  Handler->>Resume: find().sort().skip().limit().lean()
  Resume-->>Handler: resumes array
  Handler->>paginatedResponse: Call with data, total, page, limit
  paginatedResponse->>Client: JSON { success, data, meta }
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Hop along with pages neat,
Skip and sort make lists complete,
Middleware magic, response so sweet,
Pagination helpers can't be beat!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements pagination middleware and applies it to resume routes, but only partially addresses issue #515 by not applying it to jobs, alerts, and posts. Apply the pagination middleware to remaining list endpoints (jobs, alerts, posts) as specified in issue #515 objectives, or clarify if this PR addresses only the resume route.
Out of Scope Changes check ⚠️ Warning The PR includes changes to .env.example with LINKEDIN_REDIRECT_URI and GEMINI_API_KEY that are unrelated to the pagination feature objective. Remove or isolate the .env.example changes into a separate PR to keep the pagination feature scope focused and clear.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding a pagination middleware for resume routes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/.env.example`:
- Line 35: Remove the duplicate GEMINI_API_KEY entry: delete the second
definition of GEMINI_API_KEY (the occurrence shown in the diff) so there is only
one key in the file; if the intent was to provide a dummy/local value, replace
the existing GEMINI_API_KEY value in the first occurrence instead of adding a
second line, ensuring only a single GEMINI_API_KEY is present.

In `@backend/src/middleware/paginate.js`:
- Around line 19-22: The sort parsing assumes req.query.sort is a string; if
it's an array (repeated params) startsWith will throw — fix by normalizing
sortParam first (e.g., const rawSort = req.query.sort; const sortParam =
Array.isArray(rawSort) ? rawSort[0] : (rawSort || '-createdAt')); then compute
sortOrder and sortField from that normalized sortParam used by the existing
sortOrder and sortField logic in paginate middleware so startsWith and replace
are only called on a string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93dbc02a-3dc9-47cf-898f-9c02ce09d360

📥 Commits

Reviewing files that changed from the base of the PR and between 86d5f15 and 7038a1c.

📒 Files selected for processing (3)
  • backend/.env.example
  • backend/src/middleware/paginate.js
  • backend/src/routes/resume.js

Comment thread backend/.env.example
LINKEDIN_CLIENT_SECRET=your_linkedin_client_secret
LINKEDIN_REDIRECT_URI=http://localhost:5000/api/auth/linkedin/callback No newline at end of file
LINKEDIN_REDIRECT_URI=http://localhost:5000/api/auth/linkedin/callback
GEMINI_API_KEY=dummy-key-for-local-dev
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove duplicate GEMINI_API_KEY definition.

GEMINI_API_KEY is already defined on line 8. Having duplicate keys in environment files causes the last occurrence to override earlier ones, leading to configuration confusion. Developers may set the value on line 8 expecting it to work, unaware that line 35 silently overrides it.

🔧 Proposed fix

If the intent is to provide a dummy value for local development, update line 8 instead:

-GEMINI_API_KEY=your_gemini_api_key
+GEMINI_API_KEY=dummy-key-for-local-dev

And remove line 35:

 LINKEDIN_REDIRECT_URI=http://localhost:5000/api/auth/linkedin/callback
-GEMINI_API_KEY=dummy-key-for-local-dev
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 35-35: [DuplicatedKey] The GEMINI_API_KEY key is duplicated

(DuplicatedKey)


[warning] 35-35: [UnorderedKey] The GEMINI_API_KEY key should go before the LINKEDIN_CLIENT_ID key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/.env.example` at line 35, Remove the duplicate GEMINI_API_KEY entry:
delete the second definition of GEMINI_API_KEY (the occurrence shown in the
diff) so there is only one key in the file; if the intent was to provide a
dummy/local value, replace the existing GEMINI_API_KEY value in the first
occurrence instead of adding a second line, ensuring only a single
GEMINI_API_KEY is present.

Comment on lines +19 to +22
const sortParam = req.query.sort || '-createdAt';
const sortOrder = sortParam.startsWith('-') ? -1 : 1;
const sortField = sortParam.replace(/^-/, '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Express 4.x, what type is req.query.sortwhen a query string repeats the same key (e.g.,?sort=-createdAt&sort=title)?

💡 Result:

In Express 4.x, when a query string contains repeated keys (e.g.,?sort=-createdAt&sort=title), the type of req.query.sort is typically an Array of strings [1][2][3]. This behavior is provided by the default "extended" query parser, which uses the qs library under the hood [1][4]. Because req.query can contain strings, arrays, or nested objects depending on the input, it is a best practice to verify the type of the property before accessing it (e.g., checking if it is an array) to avoid runtime errors [4]. Important Version Note: In Express version 4.22.0 and 4.22.1, a regression occurred where the qs library's default arrayLimit of 20 caused repeated keys to be parsed as an object (e.g., { "0": "value1", "1": "value2" }) instead of an array when more than 20 values were provided [1][5]. This was resolved in Express 4.22.2, which restored the expected behavior of returning an array for repeated keys regardless of the count [1][5].

Citations:


🏁 Script executed:

# Check if the file exists and read the relevant section
ls -la backend/src/middleware/paginate.js 2>&1 || echo "File not found"

Repository: anurag3407/career-pilot

Length of output: 151


🏁 Script executed:

# Read lines around 19-22 to verify the actual code
head -30 backend/src/middleware/paginate.js | cat -n

Repository: anurag3407/career-pilot

Length of output: 1278


🏁 Script executed:

# Check if similar issues exist with page and limit parameters
cat -n backend/src/middleware/paginate.js | sed -n '11,16p'

Repository: anurag3407/career-pilot

Length of output: 333


Harden sort parsing against non-string query values.

Line 20 can throw when req.query.sort is an array (e.g., repeated sort params like ?sort=-createdAt&sort=title), because startsWith() is only available on strings.

Proposed fix
-    const sortParam = req.query.sort || '-createdAt';
-    const sortOrder = sortParam.startsWith('-') ? -1 : 1;
-    const sortField = sortParam.replace(/^-/, '');
+    const rawSort = Array.isArray(req.query.sort) ? req.query.sort[0] : req.query.sort;
+    const sortParam = typeof rawSort === 'string' && rawSort.trim() ? rawSort.trim() : '-createdAt';
+    const sortOrder = sortParam.startsWith('-') ? -1 : 1;
+    const sortField = sortParam.replace(/^-/, '') || 'createdAt';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/middleware/paginate.js` around lines 19 - 22, The sort parsing
assumes req.query.sort is a string; if it's an array (repeated params)
startsWith will throw — fix by normalizing sortParam first (e.g., const rawSort
= req.query.sort; const sortParam = Array.isArray(rawSort) ? rawSort[0] :
(rawSort || '-createdAt')); then compute sortOrder and sortField from that
normalized sortParam used by the existing sortOrder and sortField logic in
paginate middleware so startsWith and replace are only called on a string.

@anurag3407 anurag3407 added level:intermediate Intermediate level task quality:clean High quality code gssoc:approved Verified contribution labels May 18, 2026
@anurag3407 anurag3407 merged commit 79380bb into anurag3407:main May 18, 2026
2 checks passed
avzuha pushed a commit to avzuha/career-pilot that referenced this pull request May 21, 2026
…lper

feat: add pagination middleware for resume routes (anurag3407#515)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Verified contribution level:intermediate Intermediate level task quality:clean High quality code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] Add pagination helper for list endpoints

2 participants