feat: add pagination middleware for resume routes (#515)#642
Conversation
📝 WalkthroughWalkthroughThis 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. ChangesPagination Infrastructure and Resume Integration
Sequence DiagramsequenceDiagram
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 }
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
backend/.env.examplebackend/src/middleware/paginate.jsbackend/src/routes/resume.js
| 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 |
There was a problem hiding this comment.
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-devAnd 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.
| const sortParam = req.query.sort || '-createdAt'; | ||
| const sortOrder = sortParam.startsWith('-') ? -1 : 1; | ||
| const sortField = sortParam.replace(/^-/, ''); | ||
|
|
There was a problem hiding this comment.
🧩 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:
- 1: fix: restore array parsing for req.query repeated keys (#7147) expressjs/express#7181
- 2: https://stackoverflow.com/questions/55312311/express-js-request-problem-pass-multiple-values-collection-for-a-query-key
- 3: https://stackoverflow.com/questions/64946377/express-multiple-query-parameters-with-same-name-does-not-return-array
- 4: https://evanhahn.com/gotchas-with-express-query-parsing-and-how-to-avoid-them/
- 5: req.query will produce object instead of array when it contains more than 20 values in v4.22.X expressjs/express#7147
🏁 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 -nRepository: 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.
…lper feat: add pagination middleware for resume routes (anurag3407#515)
Summary
Closes #515
Implements a reusable pagination middleware for the CareerPilot backend.
Changes Made
backend/src/middleware/paginate.js— generic pagination middleware that extractspageandlimitfrom query params and attachesskip,limit, andpagetoreq.paginationbackend/src/routes/resume.jsto use the pagination middleware on the resume listing routeMONGODB_URIin.envHow to Test
npm run devGET /api/resumes?page=1&limit=10with a valid auth tokenType of Change
Summary by CodeRabbit
New Features
Chores