Skip to content

Conversation

@RobinTail
Copy link
Owner

@RobinTail RobinTail commented Nov 8, 2025

Due to #2938

This will set us free from the requirement to write slash to the keys when we imply method depending routing.

Breaking change: Integration requires config parameter.

Summary by CodeRabbit

  • New Features

    • Added methodLikeRouteBehavior configuration option to control interpretation of method-like routing keys ("method" or "path")
    • Added hasHeadMethod option for endpoint documentation configuration
  • Refactor

    • Integration constructor now requires a config parameter during initialization
    • Routing syntax simplified to use cleaner method-based keys

@RobinTail RobinTail added the enhancement New feature or request label Nov 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR adds configuration-driven routing behavior to the framework. A new methodLikeRouteBehavior property is introduced to CommonConfig, allowing routing keys that resemble HTTP methods to be interpreted as either method handlers or path labels. The config parameter is propagated through Integration, Documentation, and the walkRouting function to enable this behavior. Routing examples are updated to reflect method-based key syntax.

Changes

Cohort / File(s) Summary
Configuration Schema
express-zod-api/src/config-type.ts
Added optional methodLikeRouteBehavior property ("method" | "path") to CommonConfig interface with JSDoc documentation and default value.
Integration & Documentation Setup
express-zod-api/src/integration.ts, express-zod-api/src/documentation.ts
Updated constructors to accept config: CommonConfig parameter; propagated config to walkRouting calls. Added hasHeadMethod JSDoc note in Integration.
Routing Walker
express-zod-api/src/routing-walker.ts, express-zod-api/src/routing.ts
Updated RoutingWalkerParams interface and walkRouting function signature to include config parameter; modified processEntries to use methodLikeRouteBehavior setting to determine route interpretation; updated call site in routing.ts to pass config.
Documentation & Changelog
CHANGELOG.md, README.md
Updated v26.0.0 changelog entry and README to document new routing object-based syntax and config property in Integration constructor. Added @see reference to methodLikeRouteBehavior.
Examples
example/routing.ts, example/generate-client.ts
Updated routing definition to use concise method keys (e.g., patch instead of "patch /"); added config parameter to Integration instantiation.
Tests
express-zod-api/tests/integration.spec.ts, express-zod-api/tests/routing.spec.ts
Added configMock and passed it to Integration instances; introduced methodLikeRouteBehavior: "path" in routing tests with updated route definitions using method keys.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • routing-walker.ts: Core logic change determining method vs. path interpretation based on methodLikeRouteBehavior setting requires careful review to ensure correct behavior in both modes.
  • Config propagation chain: Verify config is correctly threaded through Integration → documentation → walkRouting → processEntries without loss or type mismatches.
  • Test coverage: Confirm tests adequately cover both "method" and "path" modes of methodLikeRouteBehavior and edge cases in nested routes.

Possibly related PRs

  • Removing DependsOnMethod #2938: Modifies routing handling for method-like keys, removing DependsOnMethod and standardizing routing object syntax—directly related to the method key interpretation changes.
  • feat: HEAD method handling #2816: Modifies routing machinery (routing-walker, integration, documentation) to propagate config and handle method/HEAD behavior—overlaps in constructor signatures and routing walker updates.
  • feat: configurable depiction of the HEAD method #2872: Updates Documentation/Integration constructors to add hasHeadMethod option—related through constructor signature modifications.

Suggested labels

refactoring

Poem

🐰 A config-bound route, a method in sight,
No more string keys tangled and tight!
Path or method, the choice is now clear,
Through tunnels of config, our endpoints appear.

Pre-merge checks and finishing touches

✅ 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 "feat(v26): method only routes" directly and concisely describes the main feature being introduced—method-only routing syntax without leading slashes. It accurately captures the primary change across the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

@coveralls-official
Copy link

coveralls-official bot commented Nov 8, 2025

Coverage Status

coverage: 100.0%. remained the same
when pulling b3b74f1 on feat-method-only
into 547b13f on make-v26.

@RobinTail RobinTail changed the title feat(v25): method only route feat(v26): method only route Nov 9, 2025
@RobinTail RobinTail changed the title feat(v26): method only route feat(v26): method only routes Nov 9, 2025
@RobinTail RobinTail added the breaking Backward incompatible changes label Nov 9, 2025
@RobinTail RobinTail added the documentation Improvements or additions to documentation label Nov 9, 2025
@RobinTail RobinTail marked this pull request as ready for review November 9, 2025 08:38
@RobinTail
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@RobinTail RobinTail added this to the v26 milestone Nov 9, 2025
Copy link
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: 1

🧹 Nitpick comments (9)
example/routing.ts (1)

22-23: Clarify the comment to reflect method-only routing.

The comment states "methods are defined within the route," but with the new patch syntax, the method name is the route key itself (method-only routing), not a combined string like "patch /". Consider updating the comment to clarify this distinction, for example:

-        // syntax 2: methods are defined within the route
+        // syntax 2: method-only routing (requires methodLikeRouteBehavior config)
         patch: updateUserEndpoint, // demonstrates authentication

This helps readers understand that patch is interpreted as an HTTP method, not a path segment like retrieve or remove.

CHANGELOG.md (2)

7-12: Clarify which keys are recognized as “method-like”.

Suggest listing supported lowercase HTTP methods (get, post, put, patch, delete, head, options) and noting case-sensitivity to avoid ambiguity.


21-31: Make it explicit that “create” is a nested path segment.

To prevent confusion with HTTP methods, consider annotating the example or using a more obviously non-method key:

-+   "/v1/users": {
-+     get: getUserEndpoint,
-+     create: makeUserEndpoint
-+   },
++   "/v1/users": {
++     get: getUserEndpoint,         // method
++     create: makeUserEndpoint      // nested path segment
++   },
example/generate-client.ts (1)

8-13: Guard serverUrl when listen is an object.

config.http.listen can be number|string|ListenOptions. If it’s ListenOptions, template string will yield “[object Object]”. Optionally normalize:

-    serverUrl: `http://localhost:${config.http!.listen}`,
+    serverUrl: `http://localhost:${
+      typeof config.http?.listen === "object"
+        ? (config.http.listen as { port?: number|string }).port ?? 80
+        : config.http?.listen
+    }`,
express-zod-api/tests/integration.spec.ts (1)

24-24: Add a focused test for methodLikeRouteBehavior="path".

Current tests pass config but don’t assert the “method-like key as path segment” mode. Add one snapshot where a key like “get” under a path object is forced to be treated as a nested segment via config.methodLikeRouteBehavior="path". This hardens the new behavior.

Also applies to: 29-47, 52-70, 75-89, 112-126, 138-163

express-zod-api/src/config-type.ts (1)

49-57: New config option looks good; document recognized methods.

Consider specifying recognized lowercase method names (get, post, put, patch, delete, head, options) in the JSDoc for clarity. Ensure routing-walker applies a default of "method" when unset.

express-zod-api/src/routing.ts (1)

19-21: Update example to match “DependsOnMethod removed”.

Replace the stale “dependsOnMethod” example with method-key routing:

- * @example { dependsOnMethod: { get: retrieveEndpoint, post: createEndpoint } }
+ * @example { users: { get: retrieveEndpoint, post: createEndpoint } } // methods as keys on an object
README.md (2)

308-314: Consider documenting method-only routing conditions.

The method-based routing example clearly shows the new syntax. To help users avoid confusion, consider adding a note that method-only routing (where keys like get, post are treated as methods rather than path segments) only applies when:

  1. The key is a valid HTTP method name
  2. The value is an Endpoint (not nested routing)
  3. methodLikeRouteBehavior is set to "method" (which is the default)

For nested routing under method keys, users should either use explicit syntax like "get /path" or set methodLikeRouteBehavior: "path".


1088-1092: Clarify whether config parameter is required.

The documentation shows that config is now a parameter for the Integration constructor, and the PR description mentions "Integration now requires a config parameter" as a breaking change. Consider making this explicit in the documentation by:

  • Adding a comment indicating it's required, or
  • Showing both the old usage (deprecated) and new usage with a migration note

This will help users understand the breaking change when upgrading.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 547b13f and d4049cc.

📒 Files selected for processing (11)
  • CHANGELOG.md (1 hunks)
  • README.md (2 hunks)
  • example/generate-client.ts (1 hunks)
  • example/routing.ts (1 hunks)
  • express-zod-api/src/config-type.ts (1 hunks)
  • express-zod-api/src/documentation.ts (2 hunks)
  • express-zod-api/src/integration.ts (4 hunks)
  • express-zod-api/src/routing-walker.ts (5 hunks)
  • express-zod-api/src/routing.ts (2 hunks)
  • express-zod-api/tests/integration.spec.ts (5 hunks)
  • express-zod-api/tests/routing.spec.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-07-20T11:09:58.980Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2833
File: express-zod-api/src/method.ts:3-3
Timestamp: 2025-07-20T11:09:58.980Z
Learning: In express-zod-api, the `SomeMethod` type (defined as `Lowercase<string>`) is intentionally broad to represent raw, unvalidated HTTP methods from requests. The narrower `Method` type is used after validation with `isMethod()`. This defensive programming pattern separates raw external input types from validated types, allowing graceful handling of unknown methods.

Applied to files:

  • express-zod-api/src/config-type.ts
  • express-zod-api/src/routing-walker.ts
📚 Learning: 2025-05-28T18:58:10.064Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2428
File: express-zod-api/src/index.ts:44-44
Timestamp: 2025-05-28T18:58:10.064Z
Learning: The type-only import `import type {} from "qs";` in express-zod-api/src/index.ts is necessary to avoid TS2742 errors for exported functions like attachRouting, makeRequestMock, testEndpoint, and testMiddleware that have types depending on types/qs. This import provides the reference TypeScript needs to infer portable type names.

Applied to files:

  • express-zod-api/src/config-type.ts
  • express-zod-api/src/integration.ts
  • express-zod-api/tests/integration.spec.ts
  • express-zod-api/src/routing.ts
  • express-zod-api/tests/routing.spec.ts
📚 Learning: 2025-06-14T16:42:52.972Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2736
File: express-zod-api/tsup.config.ts:12-26
Timestamp: 2025-06-14T16:42:52.972Z
Learning: In express-zod-api tsup configurations, the direct mutation of `options.supported` in the `esbuildOptions` callback is intentional behavior and should not be flagged as a side effect issue.

Applied to files:

  • express-zod-api/src/config-type.ts
  • express-zod-api/src/documentation.ts
  • express-zod-api/src/routing.ts
  • express-zod-api/tests/routing.spec.ts
📚 Learning: 2025-06-02T21:08:56.475Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:08:56.475Z
Learning: The `cjs-test` directory in the express-zod-api repository is a test workspace and should be excluded when checking for main project version consistency with changelog entries.

Applied to files:

  • express-zod-api/tests/integration.spec.ts
📚 Learning: 2025-10-02T17:42:48.840Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-10-02T17:42:48.840Z
Learning: In express-zod-api v25 (ESM-only), the `.example()` method error occurs when user code runs as CommonJS. Express Zod API patches Zod's ESM bundle with `.example()`, but CommonJS code requires a separate CJS bundle instance that lacks this patch. Users must run their code as ESM by: (1) setting `"type": "module"` in package.json, (2) using `.mts` or `.mjs` file extensions, or (3) using tools like `tsx` or `vite-node` that provide their own ESM-compatible compilation.

Applied to files:

  • express-zod-api/src/routing.ts
📚 Learning: 2025-05-27T20:03:34.213Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: example/factories.ts:35-42
Timestamp: 2025-05-27T20:03:34.213Z
Learning: The `./example` directory in the express-zod-api repository contains demonstration code for educational purposes only, not intended for production use. Example code can make simplified assumptions for brevity and clarity, and should not be flagged for missing production-level error handling, security measures, or edge case handling.

Applied to files:

  • express-zod-api/src/routing.ts
📚 Learning: 2025-05-27T19:35:57.357Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/buffer-schema.spec.ts:32-37
Timestamp: 2025-05-27T19:35:57.357Z
Learning: In the express-zod-api project, tests are run from the `express-zod-api` workspace directory, and the project uses an ESM-first environment without `__dirname`. Relative paths like `../logo.svg` in test files correctly resolve to the repository root due to this test execution context.

Applied to files:

  • express-zod-api/tests/routing.spec.ts
🔇 Additional comments (10)
express-zod-api/src/routing.ts (1)

81-82: Passing config into walkRouting — looks good.

express-zod-api/src/documentation.ts (1)

48-54: Config plumbed into Documentation — verified.

The required config and forwarding to walkRouting are correct. Verified that routing-walker properly defaults methodLikeRouteBehavior to "method" via destructuring default in routing-walker.ts:41.

express-zod-api/src/integration.ts (1)

28-31: Config now required in Integration — implementation verified complete.

Verification confirms all Integration instantiations in production code and tests supply the required config parameter. No call sites missing the property.

express-zod-api/tests/routing.spec.ts (3)

34-94: LGTM! Test correctly validates path-based interpretation.

The test properly validates that when methodLikeRouteBehavior: "path" is set, method-like keys (e.g., get, post) are treated as path segments rather than HTTP methods. The expectations correctly verify that the get key results in the path /v1/user/get.

Note: This test uses the non-default behavior. The default (methodLikeRouteBehavior: "method") is tested in subsequent tests where method keys route to the parent path without adding a segment.


112-155: LGTM! Test validates the new method-only routing feature.

This test correctly validates the default behavior (methodLikeRouteBehavior: "method") where method-like keys (get, post, put, patch) are interpreted as HTTP methods rather than path segments. The expectations confirm that all methods route to the parent path /v1/user instead of creating nested paths like /v1/user/get.

This is the core feature introduced by this PR and the test coverage is appropriate.


157-182: LGTM! Proper validation of method support.

The test correctly validates that when using method-only routing, the framework verifies that the endpoint actually supports the assigned method. The intentional assignment to post (line 169) of an endpoint that only supports put and patch properly triggers an error, ensuring type safety at runtime.

express-zod-api/src/routing-walker.ts (4)

22-27: LGTM! Type signature updated to support config-driven routing.

The addition of config: CommonConfig to RoutingWalkerParams enables the routing walker to access methodLikeRouteBehavior configuration. This is a necessary change for the method-only routing feature.

Note: This is part of the breaking change mentioned in the PR description, as all call sites of walkRouting must now provide a config parameter.


40-56: LGTM! Core logic correctly implements method-only routing.

The implementation properly handles both routing modes:

  1. Method mode (default, methodLikeRouteBehavior: "method"): When a method-like key (e.g., get) is assigned to an AbstractEndpoint, the key is interpreted as the HTTP method and the segment is "/", resulting in routing to the parent path.

  2. Path mode (methodLikeRouteBehavior: "path"): Method-like keys are treated as path segments, falling through to detachMethod which returns them as regular path components.

The three-part condition on lines 49-50 ensures method-only routing only activates when:

  • The key is a valid HTTP method
  • The config enables method interpretation
  • The value is an actual endpoint (not nested routing)

This prevents confusion when method-like keys are used with nested routing structures.


40-42: Verify: Default value is breaking change.

The default methodLikeRouteBehavior = "method" enables the new method-only routing behavior. This means that existing route definitions using method names as path segments (e.g., { get: { nested: endpoint } } intending /get/nested) will now be interpreted differently.

Ensure this breaking change is:

  1. ✅ Documented in the PR description
  2. ✅ Mentioned in CHANGELOG/migration guide
  3. Communicated in release notes

Users upgrading will need to either:

  • Set methodLikeRouteBehavior: "path" to restore old behavior, or
  • Rename their routes to avoid method names

86-116: LGTM! Config properly propagated through routing traversal.

The walkRouting function correctly threads the config parameter through both the initial processEntries call (line 92) and the recursive call when expanding nested routing structures (line 113). This ensures that methodLikeRouteBehavior is consistently applied at all nesting levels.

The function maintains all existing validation logic (duplicate checking, method support checking) while integrating the new config-driven behavior seamlessly.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Owner Author

@RobinTail RobinTail left a comment

Choose a reason for hiding this comment

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

I like it

@RobinTail RobinTail merged commit 77c82ee into make-v26 Nov 9, 2025
13 checks passed
@RobinTail RobinTail deleted the feat-method-only branch November 9, 2025 15:38
@RobinTail RobinTail mentioned this pull request Nov 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Backward incompatible changes documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants