parse-server icon indicating copy to clipboard operation
parse-server copied to clipboard

feat: Bump path-to-regexp to v8

Open coratgerl opened this issue 4 weeks ago • 17 comments

Pull Request

Issue

Fixes: https://github.com/parse-community/parse-server/issues/9402

This code snippet transforms wildcard characters (*) in route paths into named parameters compatible with path-to-regexp v8. The transformation is necessary because path-to-regexp v8 no longer supports unnamed wildcards (*) due to security and design changes.

Key Logic

  • The code scans the route.requestPath for any * characters.
  • It only transforms standalone * (e.g., /files/*/files/*path).
  • It preserves * in named parameters (e.g., :id* or *id) to avoid breaking existing functionality.
  • The transformation ensures compatibility with path-to-regexp v8 while maintaining backward compatibility for routes using named wildcards.

Why?

  • Security: Unnamed wildcards can lead to vulnerable regex patterns (e.g., ReDoS).
  • Compatibility: path-to-regexp v8 requires named parameters for wildcards, so this transformation bridges the gap for existing codebases.

Example

// Before
'/files/*' → transformed to '/files/*path'
// Unchanged
'/users/\:id*' → remains '/users/\:id*'
'/search*query' → remains '/search*query'

Summary by CodeRabbit

  • Chores

    • Updated path-to-regexp dependency to v8.3.0.
  • Refactor

    • Standardized route pattern syntax to path-to-regexp v8 across routing and rate limiting.
    • Simplified rate-limit pattern compilation and matching to use direct path patterns.
  • Bug Fixes / Validation

    • Added validation with clear errors for deprecated wildcard route syntax.
  • Tests

    • Updated rate-limit tests and error expectations to align with new syntax.
  • Documentation

    • Added migration guide and clarified rate-limit path examples.

✏️ Tip: You can customize this high-level summary in your review settings.

coratgerl avatar Nov 23 '25 16:11 coratgerl

I will reformat the title to use the proper commit message syntax.

🚀 Thanks for opening this pull request!

:white_check_mark: Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
:white_check_mark: Open Source Security 0 0 0 0 0 issues

:computer: Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

parseplatformorg avatar Nov 23 '25 16:11 parseplatformorg

📝 Walkthrough

Walkthrough

Standardizes route path handling to path-to-regexp v8: dependency bumped; cloud route strings use brace-based catch-alls; rate-limit compilation/validation now uses path-to-regexp v8; tests, options docs, and a migration guide updated to v8 syntax.

Changes

Cohort / File(s) Summary
Dependency
package.json
Bumped path-to-regexp from 6.3.0 to 8.3.0.
Cloud route patterns
src/cloud-code/Parse.Cloud.js
Switches cloud route construction to brace-based catch-all syntax (e.g., {/*id}) replacing prior /:id?(.*)-style fragments.
Rate limit matching & validation
src/middlewares.js, src/Config.js, spec/RateLimit.spec.js
- src/Config.js: imports pathToRegexp and validates rateLimit.requestPath by compiling with path-to-regexp v8, throwing a descriptive error on invalid syntax. - src/middlewares.js: matching prefers limit.path.regexp if present; addRateLimit now compiles route.requestPath with pathToRegexp(route.requestPath) (removed prior transformPath). - spec/RateLimit.spec.js: test requestPath values updated to named wildcard forms (e.g., /functions/*path) and a thrown string replaced with new Error(...).
Options / Docs / Migration guide
src/Options/index.js, src/Options/Definitions.js, src/Options/docs.js, 9.0.0.md
Documentation and option help updated to require path-to-regexp v8 syntax with named wildcards (examples: /functions/*path, /classes/MyClass/*path, /*path); migration guidance added.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AddRateLimit as addRateLimit
    participant Config as Config.validateRateLimit
    participant P2R as pathToRegexp
    participant RateHandler as handleRateLimit

    Client->>AddRateLimit: register route(requestPath)
    AddRateLimit->>Config: validateRateLimit(requestPath)
    alt invalid v8 syntax
        Config-->>AddRateLimit: throw validation error
    else valid v8 pattern
        Config->>P2R: compile requestPath
        P2R-->>Config: compiled regexp
        Config-->>AddRateLimit: attach compiled regexp (limit.path.regexp)
    end
    AddRateLimit->>RateHandler: store limit (with .path/.path.regexp)
    Client->>RateHandler: incoming request
    RateHandler->>RateHandler: match using limit.path.regexp if present else fallback to limit.path
    RateHandler->>RateHandler: apply rate limiting logic

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Files meriting extra attention:
    • src/cloud-code/Parse.Cloud.js — verify brace-based route formatting covers special cases (@File, classes, @Config) and matches runtime routing expectations.
    • src/Config.js — confirm validation semantics, error messages, and behavior for array vs. single rateLimit entries.
    • src/middlewares.js — ensure compiled regexp selection and matching logic preserve existing semantics and handle legacy entries.
    • spec/RateLimit.spec.js — confirm test updates align with path-to-regexp v8 semantics.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description partially fills the template with a linked issue but lacks structured sections for Approach and Tasks as specified in the template. Complete the Approach section with a clear overview of changes, and mark completed/applicable tasks with checkboxes to fully comply with the template structure.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR successfully addresses the core requirement of upgrading path-to-regexp to v8 [#9402] by transforming unnamed wildcards to named parameters, adding validation, updating documentation, and providing a migration guide.
Out of Scope Changes check ✅ Passed All changes are within scope of path-to-regexp v8 upgrade: dependency bump, route pattern updates, validation logic, documentation updates, and migration guide. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately summarizes the main change: updating route patterns to use path-to-regexp v8 syntax, which is the core objective across all modified files.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

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.

coderabbitai[bot] avatar Nov 23 '25 16:11 coderabbitai[bot]

Codecov Report

:x: Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review. :white_check_mark: Project coverage is 92.56%. Comparing base (7bb548b) to head (289eb59). :warning: Report is 16 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Config.js 75.00% 1 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #9942      +/-   ##
==========================================
- Coverage   92.58%   92.56%   -0.02%     
==========================================
  Files         191      191              
  Lines       15559    15560       +1     
  Branches      177      177              
==========================================
- Hits        14405    14403       -2     
- Misses       1142     1145       +3     
  Partials       12       12              

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

codecov[bot] avatar Nov 23 '25 16:11 codecov[bot]

@coderabbitai

The transformation ensures compatibility with path-to-regexp v8 while maintaining backward compatibility for routes using named wildcards.

This sounds like a workaround; what changes are needed in the codebase to avoid doing this complex path transformation?

mtrezza avatar Nov 23 '25 16:11 mtrezza

[!TIP] For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's examine the actual changes in middlewares.js to see the transformation logic
git diff origin/alpha HEAD -- src/middlewares.js

Length of output: 4556


🏁 Script executed:

#!/bin/bash
# Check the changes in Parse.Cloud.js
git diff origin/alpha HEAD -- src/cloud-code/Parse.Cloud.js

Length of output: 584


🏁 Script executed:

#!/bin/bash
# Search for where rate limit paths are defined/configured in the codebase
rg -n "path.*:" --type=js -A 2 -B 2 -g '!node_modules' -g '!dist' | head -100

Length of output: 4900


🏁 Script executed:

#!/bin/bash
# Look for configuration files or documentation about rate limits
fd -e md -e json -e js | rg -i "rate.*limit|config" | head -20

Length of output: 596


🌐 Web query:

path-to-regexp version 8 breaking changes migration guide

💡 Result:

Short migration checklist for path-to-regexp v8 (what changed, and how to fix your routes)

  1. Wildcards (*) must be named
  • Old (v6/v7/Express‑4): app.get('', handler) or app.use('/', ...)
  • New (v8 / Express‑5): name the wildcard or use a regex:
    • Named wildcard: '/*name' (captures segments into req.params.name as an array).
    • To include root ('/') as an empty capture use '/{*name}'.
    • Or use a RegExp: /(.*)/.
      References: README (wildcard examples) and Express issue/migration notes. [1][2]
  1. Inline sub‑expression regex in route strings removed / stricter parsing
  • Patterns like '/:id(\d+)' or other inline sub‑expressions are no longer supported (security/ReDoS hardening). Move validation into middleware or use a RegExp route. [3][2]
  1. Special characters and optional pieces changed
  • Characters like ?, +, *, (), [] are handled differently. Optional segments should use braces (e.g. '/:file{.:ext}'), not the old '?' token in many cases. Check and rewrite routes that relied on loose parsing. [1][3]
  1. Parameter naming/validation stricter
  • Parameters must be valid identifiers (or double‑quoted) and cannot be left nameless. If you get "Missing parameter name" errors, find routes containing bare '*' or malformed parameter tokens and rename them. [1][2]
  1. Wildcard captures are arrays
  • A named wildcard (/*splat) returns an array of segments (e.g. ['a','b']) — join if you need the full path. [1][2]

Quick migration steps

  • Run tests and start your app; search logs for "Missing parameter name" or route parse errors.
  • Replace app.get('', ...) / '/' usages with either '/*name', '/{name}', or /(.)/.
  • Replace inline regex route tokens (/:id(\d+)) with validation middleware or RegExp routes.
  • Audit any routes using ?, +, () etc. and convert to the v8 syntax (use braces for optional segments when appropriate).
  • Re-run tests and exercise all route code paths.

References

  • path-to-regexp README (usage, wildcards, params). [1]
  • Express v5 release / migration notes (path matching changes, upgrade to path-to-regexp@8). [3]
  • Express issue discussing "Missing parameter name" and example fixes. [2]

If you want, I can scan your codebase (or a sample of your routes) and produce exact replacement edits.


🏁 Script executed:

#!/bin/bash
# Check the rate limit test specs to see how users define paths
cat spec/RateLimit.spec.js | head -200

Length of output: 6393


🏁 Script executed:

#!/bin/bash
# Search for rate limit configuration examples in documentation
fd -e md | xargs rg -l "rateLimit" | head -5

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Look for where requestPath is used/defined in rate limit configuration
rg -n "requestPath" --type=js -C 5 -g '!node_modules' -g '!dist'

Length of output: 26222


@mtrezza The complex transformation exists primarily for backward compatibility with user-provided rate limit configurations. Here's what's happening and the alternatives:

Current Situation

Users configure rate limits with paths using the old wildcard syntax:

rateLimit: {
  requestPath: '/functions/*',  // Old syntax
  requestTimeWindow: 10000,
  requestCount: 1
}

The transformation in middlewares.js converts these to path-to-regexp v8 compatible syntax by replacing standalone * with *path (a named wildcard).

Alternatives to Avoid the Transformation

1. Breaking Change - Require Users to Migrate (Cleanest approach)

Force users to update their configurations to use v8 syntax:

// Old (no longer supported)
requestPath: '/functions/*'
requestPath: '*'

// New (required)
requestPath: '/functions/*path'
requestPath: '*path'

Changes needed:

  • Remove the transformation logic in middlewares.js
  • Add configuration validation to reject old syntax
  • Document the breaking change in migration guide
  • Update all test specs to use new syntax

2. Transform at Configuration Time (Simpler & earlier validation)

Move the transformation to Config.js where rate limits are validated:

// In Config.js validateRateLimit function
static validateRateLimit(options) {
  // ... existing validation ...
  
  // Transform and validate path syntax here
  option.requestPath = transformPathToV8(option.requestPath);
  
  // Validate the transformed path works with pathToRegexp
  try {
    pathToRegexp(option.requestPath);
  } catch (e) {
    throw `Invalid requestPath: ${e.message}`;
  }
}

This way the transformation happens once during server initialization, and middlewares.js works with pre-transformed paths.

3. Store Compiled Regex (Best performance)

Instead of storing the path string and re-compiling repeatedly, compile once:

// In addRateLimit (middlewares.js)
config.rateLimits.push({
  path: {
    original: route.requestPath,
    regexp: pathToRegexp(transformPath)  // Compile once, store the regex
  },
  handler: rateLimit({...})
});

// In handleRateLimit
const pathExp = limit.path.regexp;  // Already compiled
if (pathExp.test(req.url)) { ... }

This is already partially implemented (note line 324: const pathExp = limit.path.regexp || limit.path;)

4. Deprecation Path (Most user-friendly)

Support both syntaxes temporarily with warnings:

function transformAndValidatePath(path) {
  const hasOldSyntax = /(?<![:\w])\*(?![:\w])/.test(path);
  if (hasOldSyntax) {
    logger.warn(
      `Deprecation warning: Rate limit path "${path}" uses old wildcard syntax. ` +
      `Please update to path-to-regexp v8 syntax (e.g., "/*path" instead of "/*")`
    );
    return transformPath(path);  // Apply transformation
  }
  return path;  // Already v8 compatible
}

Recommendation

I'd suggest Option 2 (transform at config time) combined with Option 4 (deprecation warnings) as the best path forward:

  1. Move transformation to Config.js - simpler, runs once
  2. Add deprecation warnings for old syntax
  3. Document the new syntax in configuration docs
  4. Plan to remove transformation in next major version

Would you like me to search for the exact location in Config.js where this should be implemented?


🧠 Learnings used
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

coderabbitai[bot] avatar Nov 23 '25 16:11 coderabbitai[bot]

@coderabbitai, we'll release the new major version of Parse Server 9 shortly, so it would be a good opportunity to add breaking changes to the release. The option "1. Breaking Change - Require Users to Migrate (Cleanest approach)" sounds like a good candidate and the breaking change sounds simple enough, so that developers can migrate to the new rate limit syntax.

mtrezza avatar Nov 23 '25 16:11 mtrezza

[!TIP] For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Examine the current transformation logic in middlewares.js
echo "=== Current transformation logic in middlewares.js ==="
sed -n '280,360p' src/middlewares.js

echo -e "\n=== Rate limit validation in Config.js ==="
rg -n "validateRateLimit|requestPath" src/Config.js -A 5 -B 2

echo -e "\n=== All rate limit test cases using wildcards ==="
rg -n "requestPath.*\*" spec/RateLimit.spec.js -B 2 -A 1

echo -e "\n=== Check if there's existing migration documentation ==="
fd -e md | xargs rg -l "migration|breaking" | head -10

Length of output: 5457


🏁 Script executed:

#!/bin/bash
# Get the actual transformation logic in addRateLimit
echo "=== addRateLimit function with transformation logic ==="
rg -n "const addRateLimit|function addRateLimit" src/middlewares.js -A 50 | head -80

echo -e "\n=== Count wildcard usage patterns in tests ==="
echo "Pattern: /functions/* count:"
rg -c "requestPath.*'/functions/\*'" spec/RateLimit.spec.js
echo "Pattern: /classes/* count:"
rg -c "requestPath.*'/classes/\*'" spec/RateLimit.spec.js
echo "Pattern: * count:"
rg -c "requestPath.*'\*'" spec/RateLimit.spec.js

echo -e "\n=== Check Options/index.js for rateLimit documentation ==="
rg -n "rateLimit" src/Options/index.js -A 10 -B 2 | head -50

Length of output: 4282


@mtrezza Perfect! Let's implement the breaking change for Parse Server 9. Here's a comprehensive implementation plan:

Implementation Plan

1. Remove Transformation Logic in src/middlewares.js

Remove lines 564-586 (the entire transformation block) and simplify to:

export const addRateLimit = (route, config, cloud) => {
  if (typeof config === 'string') {
    config = Config.get(config);
  }
  for (const key in route) {
    if (!RateLimitOptions[key]) {
      throw `Invalid rate limit option "${key}"`;
    }
  }
  if (!config.rateLimits) {
    config.rateLimits = [];
  }
  const redisStore = {
    connectionPromise: Promise.resolve(),
    store: null,
  };
  if (route.redisUrl) {
    const log = config?.loggerController || defaultLogger;
    const client = createClient({
      url: route.redisUrl,
    });
    client.on('error', err => { log.error('Middlewares addRateLimit Redis client error', { error: err }) });
    client.on('connect', () => { });
    client.on('reconnecting', () => { });
    client.on('ready', () => { });
    redisStore.connectionPromise = async () => {
      if (client.isOpen) {
        return;
      }
      try {
        await client.connect();
      } catch (e) {
        log.error(`Could not connect to redisURL in rate limit: ${e}`);
      }
    };
    redisStore.connectionPromise();
    redisStore.store = new RedisStore({
      sendCommand: async (...args) => {
        await redisStore.connectionPromise();
        return client.sendCommand(args);
      },
    });
  }
  
  // Validate path syntax with path-to-regexp
  let pathExp;
  try {
    pathExp = pathToRegexp(route.requestPath);
  } catch (error) {
    throw `Invalid rate limit requestPath "${route.requestPath}": ${error.message}`;
  }
  
  config.rateLimits.push({
    path: {
      original: route.requestPath,
      regexp: pathExp
    },
    handler: rateLimit({
      windowMs: route.requestTimeWindow,
      max: route.requestCount,
      message: route.errorResponseMessage || 'Too many requests, please try again later.',
      handler: (request, response, next, options) => {
        throw new Parse.Error(Parse.Error.CONNECTION_FAILED, options.message);
      },
      skip: (request) => {
        if (request.ip === '127.0.0.1' && !route.includeInternalRequests) {
          return true;
        }
        if (route.includeMasterKey === false && request.auth?.isMaster) {
          return true;
        }
        return false;
      },
      keyGenerator: async (request) => {
        if (route.requestMethods) {
          if (Array.isArray(route.requestMethods)) {
            if (!route.requestMethods.includes(request.method)) {
              return;
            }
          } else {
            const regExp = new RegExp(route.requestMethods);
            if (!regExp.test(request.method)) {
              return;
            }
          }
        }
        if (cloud && route.zone === RateLimitZone.global) {
          return request.config.appId;
        }
        if (cloud && route.zone === RateLimitZone.session && request.info) {
          return request.info.sessionToken;
        }
        if (cloud && route.zone === RateLimitZone.user && request.auth) {
          if (!request.auth.user) {
            const SessionClass = request.config.database.controller.adapter.getClass('_Session');
            const TokenClass = Parse.Object.extend('_Session');
            const session = await new Parse.Query(TokenClass)
              .equalTo('sessionToken', request.info.sessionToken)
              .first({ useMasterKey: true });
            request.auth.user = new Parse.User();
            request.auth.user.id = session.get('user').id;
          }
          return request.auth.user.id;
        }
        return request.ip;
      },
      store: redisStore.store,
    }),
  });
  if (cloud) {
    cloud.rateLimitsEnabled = true;
  }
};

2. Add Validation in src/Config.js

Update the validateRateLimit function to detect and reject old syntax (around line 684):

if (option.requestPath == null) {
  throw `rateLimit.requestPath must be defined`;
}
if (typeof option.requestPath !== 'string') {
  throw `rateLimit.requestPath must be a string`;
}

// Validate path-to-regexp v8 syntax
// Check for common old syntax patterns
const oldWildcardPattern = /(?:^|\/)\*(?:\/|$)/;  // Matches /* or * at start/end
const nakedWildcard = /^[\s]*\*[\s]*$/;  // Matches bare *
if (oldWildcardPattern.test(option.requestPath) || nakedWildcard.test(option.requestPath)) {
  throw `rateLimit.requestPath "${option.requestPath}" uses deprecated wildcard syntax. ` +
        `Please update to path-to-regexp v8 syntax. Examples:\n` +
        `  Old: "/functions/*" → New: "/functions/*path"\n` +
        `  Old: "/classes/*" → New: "/classes/*path"\n` +
        `  Old: "*" → New: "/*path"\n` +
        `See: https://github.com/pillarjs/path-to-regexp#usage`;
}

// Validate that the path is valid path-to-regexp syntax
try {
  pathToRegexp(option.requestPath);
} catch (error) {
  throw `rateLimit.requestPath "${option.requestPath}" is not valid: ${error.message}`;
}

3. Update All Test Cases in spec/RateLimit.spec.js

Replace all 13 occurrences:

// Old → New
requestPath: '/functions/*'      → requestPath: '/functions/*path'
requestPath: '/classes/*'        → requestPath: '/classes/*path'
requestPath: '/classes/Test/*'   → requestPath: '/classes/Test/*path'
requestPath: '*'                 → requestPath: '/*path'

4. Update Documentation in src/Options/index.js

Update the RateLimitOptions interface documentation (around line 353):

export interface RateLimitOptions {
  /* The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings or string patterns following path-to-regexp v8 syntax. Wildcards must be named (e.g., `/*path` instead of `/*`). Examples: `/functions/*path`, `/classes/MyClass/*path`, `/*path`. See: https://github.com/pillarjs/path-to-regexp */
  requestPath: string;
  /* The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied. */
  requestTimeWindow: ?number;
  /* The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied. */

5. Add Migration Guide Entry

Add to DEPRECATIONS.md (or create a MIGRATION_V9.md):

## Rate Limit Configuration Syntax

**Breaking Change**: Rate limit `requestPath` configuration now requires path-to-regexp v8 syntax.

### Migration Required

Wildcard paths must use named wildcards:

| Old Syntax (Parse Server 8) | New Syntax (Parse Server 9) |
|------------------------------|------------------------------|
| `requestPath: '*'`           | `requestPath: '/*path'`      |
| `requestPath: '/functions/*'`| `requestPath: '/functions/*path'` |
| `requestPath: '/classes/*'`  | `requestPath: '/classes/*path'` |

### Example

**Before:**
```javascript
rateLimit: {
  requestPath: '/functions/*',
  requestTimeWindow: 10000,
  requestCount: 5
}

After:

rateLimit: {
  requestPath: '/functions/*path',
  requestTimeWindow: 10000,
  requestCount: 5
}

The wildcard name (*path in the examples) can be any valid identifier (e.g., *splat, *rest, *anything).

See path-to-regexp documentation for more details.


### 6. Files to Update

1. ✅ `src/middlewares.js` - Remove transformation, add validation
2. ✅ `src/Config.js` - Add validation for old syntax
3. ✅ `spec/RateLimit.spec.js` - Update all 13 test cases
4. ✅ `src/Options/index.js` - Update documentation
5. ✅ `DEPRECATIONS.md` or `docs/migration-guide.md` - Add migration guide

### 7. Suggested PR Title

Based on the retrieved learnings, here's an Angular-style commit convention title:

feat(rate-limit)!: require path-to-regexp v8 syntax for rate limit paths


The `!` indicates a breaking change. Alternative scopes could be `config` or `api`.

Would you like me to generate the complete code changes as a diff that can be applied to the PR?

---

<details>
<summary>🧠 Learnings used</summary>

Learnt from: mtrezza Repo: parse-community/parse-server PR: 0 File: :0-0 Timestamp: 2025-11-17T15:02:48.786Z Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.


---

Learnt from: mtrezza Repo: parse-community/parse-server PR: 0 File: :0-0 Timestamp: 2025-11-17T15:02:24.824Z Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.


---

Learnt from: mtrezza Repo: parse-community/parse-server PR: 0 File: :0-0 Timestamp: 2025-11-08T13:46:04.940Z Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.


---

Learnt from: mtrezza Repo: parse-community/parse-server PR: 0 File: :0-0 Timestamp: 2025-11-08T13:46:04.940Z Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.


</details>

coderabbitai[bot] avatar Nov 23 '25 17:11 coderabbitai[bot]

@coratgerl instead of applying a patch we should fix the underlying issue, because otherwise we're just accumulating debt that eventually comes due in the future.

mtrezza avatar Nov 23 '25 17:11 mtrezza

@coratgerl instead of applying a patch we should fix the underlying issue, because otherwise we're just accumulating debt that eventually comes due in the future.

You're right, it does look better now. I assume you’ll be writing a migration guide? Should I not include these changes in my PR, then? We can add them later to the migration guide, right?

coratgerl avatar Nov 23 '25 18:11 coratgerl

You can add a migration guide for PS9, following the style of the existing PS8 guide.

mtrezza avatar Nov 23 '25 19:11 mtrezza

@coderabbitai what is this path-to-regexp v8 syntax? Is that a custom syntax or does it follow any wider known syntax? What is it similar to, regex, glob, etc?

mtrezza avatar Nov 23 '25 19:11 mtrezza

[!TIP] For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🌐 Web query:

path-to-regexp v8 syntax documentation pattern matching

💡 Result:

Summary of path-to-regexp v8 pattern syntax and matching (concise)

  • Core tokens: text, parameter (named with :name), wildcard (prefixed *), and group. Use parse/match/compile/pathToRegexp to work with paths. (app.unpkg.com)

  • Named parameters: ":name" matches until the next prefix/delimiter (default pattern [^/]+). You can quote names as :"param-name". (app.unpkg.com)

  • Modifiers:

    • Optional: suffix ? (parameter may be omitted).
    • Zero-or-more: * (can match 0+ segments).
    • One-or-more: + (must match 1+ segments).
      Example: "/:foo/:bar?" "/:foo*" "/:foo+". (socket.dev)
  • Wildcards: "/*splat" produces an array of segments for splat. (app.unpkg.com)

  • Custom regex for a param: "/user/:id(\d+)" — overrides default token pattern. (socket.dev)

  • Optional path sections (groups): use braces to make sections optional, e.g. "/users{/:id}/delete". (app.unpkg.com)

  • Important options (pathToRegexp / match / compile):

    • sensitive (case sensitive), end (must match to end), delimiter (segment delimiter, default '/'), trailing (allow optional trailing delimiter). Also encode/decode options for compile/match. (app.unpkg.com)
  • Token API: parse -> TokenData (tokens + originalPath); tokens can be turned into RegExp or generator functions (tokensToRegExp / tokensToFunction). (app.unpkg.com)

Examples

  • match("/foo/:id") -> params.id from "/foo/123". (app.unpkg.com)
  • match("/*splat") -> params.splat = ["bar","baz"] for "/bar/baz". (app.unpkg.com)
  • compile("/user/:id(\d+)")({id:123}) -> "/user/123". (app.unpkg.com)

If you want, I can fetch the exact README section or link specific examples from the v8 package files.

Citations:

  • 1: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 2: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 3: https://socket.dev/npm/package/path-to-regexp/overview/3.1.0?utm_source=openai
  • 4: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 5: https://socket.dev/npm/package/path-to-regexp/overview/3.1.0?utm_source=openai
  • 6: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 7: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 8: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 9: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 10: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai
  • 11: https://app.unpkg.com/path-to-regexp/files/Readme.md?utm_source=openai

@mtrezza path-to-regexp uses its own custom routing pattern syntax, though it's widely adopted in the JavaScript ecosystem. Here's how it relates to other pattern systems:

What is it?

Custom routing syntax - path-to-regexp defines its own DSL (Domain-Specific Language) specifically for URL route matching. It's not a standard like regex or glob, but it has become a de facto standard in Node.js web frameworks.

Most Similar To

Express.js routing patterns - In fact, Express.js uses path-to-regexp internally for route matching. If you've written Express routes, you've used this syntax:

// Express routing (powered by path-to-regexp)
app.get('/users/:id', handler);           // Named parameter
app.get('/files/*', handler);             // Wildcard (old syntax)
app.get('/posts/:id?', handler);          // Optional parameter

Relationship to Other Systems

System Similarity Key Differences
Regex Can embed custom regex patterns Simplified, URL-focused syntax; not full regex power
Glob Uses * for wildcards Globs are file-system oriented; path-to-regexp is URL-oriented
Express routing Nearly identical Express uses path-to-regexp under the hood
OpenAPI/Swagger paths Similar parameter syntax (:id) OpenAPI uses {id} instead of :id

Syntax Elements

The core tokens include text, named parameters (:name), wildcards (*name), and optional groups with braces:

// Named parameters
'/users/:id'                    // Matches: /users/123

// Wildcards (v8 requires naming)
'/files/*path'                  // Matches: /files/a/b/c

// Modifiers
'/posts/:id?'                   // Optional: ? 
'/tags/:name+'                  // One-or-more: +
'/categories/:slug*'            // Zero-or-more: *

// Custom regex within parameters
'/user/:id(\\d+)'              // Only matches digits

// Optional groups
'/users{/:id}/profile'         // Matches: /users/profile OR /users/123/profile

Why It Exists

path-to-regexp bridges the gap between simple string matching and full regex complexity. It provides:

  • Simpler syntax than regex for common routing patterns
  • Named parameter extraction (not just matching)
  • URL-specific semantics (segments, trailing slashes, etc.)
  • Type safety for route compilation

Ecosystem Adoption

Used by popular frameworks:

  • Express.js (v4 uses path-to-regexp v6, v5 uses v8)
  • Koa.js
  • React Router (similar concepts)
  • Many API routing libraries

So while it's a custom syntax, it's become a widely-recognized standard in the JavaScript/Node.js routing ecosystem, much like how regex is a standard for general pattern matching.


🧠 Learnings used
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.

coderabbitai[bot] avatar Nov 23 '25 19:11 coderabbitai[bot]

@coderabbitai will express framework also adopt the new v8 syntax, or did it already adopt it?

mtrezza avatar Nov 23 '25 19:11 mtrezza

[!TIP] For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🌐 Web query:

Express.js v5 release path-to-regexp v8

💡 Result:

Short summary — what happened and what to do

  • Express v5 was released and its router now depends on path-to-regexp v8 (upgrade from old 0.x/2.x). This causes stricter path parsing and several breaking changes. [1][2]

  • Key breaking changes from path-to-regexp v8 in Express 5:

    • Inline "sub-expression" regex in route params (e.g. /:id(\d+)) is no longer supported (removed for ReDoS protection). [1][2]
    • Wildcards and splats semantics changed: bare * or unnamed globs now error (you must name them or use explicit capture like /(.*) or a named wildcard such as /*path or name depending on syntax). Typical app.get('') can produce "Missing parameter name". [1][3]
    • Some characters ( (, ), [, ], ?, +, ! ) and group suffixes changed/reserved; optional/quantifier behaviors differ. See path-to-regexp changelog for exact token rules. [2]
  • Migration / immediate fixes

    1. Replace unnamed wildcards:
      • app.get('', handler) -> app.get(/(.)/, handler) or app.get('/*path', handler) (use named capture).
    2. Replace inline regex params by extracting param and validating with a validator (e.g., Joi/zod) or validate manually:
      • From: app.get('/:id(\d+)', …)
      • To: app.get('/:id', …) and validate req.params.id with an input-validation lib.
    3. Run your test-suite and search codebase for route patterns using (, ), *, +, ? and adjust per path-to-regexp v8 rules.
    4. Consult Express v5 migration guide and path-to-regexp README/changelog for edge cases. [1][2][3]

References

  • Express v5 release / migration notes. [1]
  • pillarjs/path-to-regexp releases & changelog (v8 details). [2]
  • Example GitHub issue showing "Missing parameter name" with app.all('*') on Express v5. [3]

@mtrezza Yes, Express v5 has already been released and it uses path-to-regexp v8. This was an upgrade from the old 0.x/2.x versions used in Express v4.

Express v5 Breaking Changes

Express v5 developers face the same migration requirements:

  1. Unnamed wildcards no longer work

    // Express v4 (path-to-regexp 0.x)
    app.get('*', handler);              // Works
    
    // Express v5 (path-to-regexp v8)
    app.get('*', handler);              // Error: "Missing parameter name"
    app.get(/(.*)/, handler);           // Fix: Use regex
    app.get('/*path', handler);         // Fix: Named wildcard
    
  2. Inline regex in route params removed (for ReDoS protection)

    // Express v4
    app.get('/:id(\\d+)', handler);     // Works
    
    // Express v5
    app.get('/:id', handler);           // Must validate in middleware/handler
    
  3. Stricter parsing of special characters (, ), [, ], ?, +, !

Alignment with Parse Server 9

This means Parse Server 9's migration to path-to-regexp v8 syntax aligns with the broader Node.js ecosystem. Developers already familiar with Express v5 migration will recognize the same patterns.

The breaking change you're introducing is consistent with what Express (the most widely-used Node.js framework) has already required from their users. This makes the migration more justifiable and the new syntax more recognizable to the community.


🧠 Learnings used
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: For new Parse Server options, verify that the option is documented in src/Options/index.js and that npm run definitions has been executed to reflect changes in src/Options/docs.js and src/Options/Definitions.js. README.md documentation is a bonus but not required for new options.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:48.786Z
Learning: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description. Common types include feat, fix, perf, refactor, docs, test, chore. The scope should identify the subsystem (e.g., graphql, rest, push, security). The description should be action-oriented and clearly convey the change's impact to developers.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-17T15:02:24.824Z
Learning: For Parse Server PRs, always suggest an Angular-style PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion with every new commit to the PR.

coderabbitai[bot] avatar Nov 23 '25 19:11 coderabbitai[bot]

Parse Server migrated to express 5 some months ago, in the migration guide we can simply refer to the express migration guide which is the same topic.

mtrezza avatar Nov 23 '25 19:11 mtrezza

@coratgerl could you please fix the conflicts?

mtrezza avatar Dec 12 '25 02:12 mtrezza

BREAKING CHANGE: Route pattern syntax across cloud routes and rate-limiting now use the new path-to-regexp v8 syntax; see the migration guide for more details.

mtrezza avatar Dec 12 '25 18:12 mtrezza

🎉 This change has been released in version 9.0.0-alpha.5

parseplatformorg avatar Dec 12 '25 18:12 parseplatformorg

🎉 This change has been released in version 9.0.0

parseplatformorg avatar Dec 14 '25 00:12 parseplatformorg