Skip to content

Support nesting zio-http routes under a prefix - #5436

Open
adamw wants to merge 1 commit into
masterfrom
fix-5435-ziohttp-nest
Open

Support nesting zio-http routes under a prefix#5436
adamw wants to merge 1 commit into
masterfrom
fix-5435-ziohttp-nest

Conversation

@adamw

@adamw adamw commented Jul 28, 2026

Copy link
Copy Markdown
Member

Fixes #5435.

Problem

zio-http's Routes.nest(prefix) (and PathCodec./) only rewrites the route patternRoutePattern.nest is copy(pathCodec = prefix ++ pathCodec). The Request passed to the handler keeps its full path.

The tapir interpreter uses zio-http routing only to dispatch (Route.handledIgnoreParams) and then re-matches the whole path itself, from req.url.path.segments. So after .nest("api"), an endpoint declared as path[UUID] (one segment) is matched against /api/<uuid> (two segments), and nothing matches.

The reported symptom is 405 Method Not Allowed rather than 404: with two or more endpoints the reject interceptor is active, and the first failure of the non-matching-method endpoint is on its FixedMethod input, so DefaultRejectHandler reports a method mismatch. With a single endpoint (reject interceptor disabled) the same setup returns 404. Nesting was broken in general, not just for this combination.

Fix

The interpreter now computes, per request, how many leading path segments come from the nesting prefix, and skips them when matching. This can only be done per-request, as the pattern under which a route ends up being registered is not known when the route is created.

  • ZioHttpInterpreter: the generated route pattern now carries its shape (number of matched segments, and whether it ends with a trailing wildcard).

    • without a trailing wildcard: context = requestSegments - patternSegments
    • with one (paths, noTrailingSlash, or endpoints with no path inputs): the wildcard absorbs the rest of the path, so its length is needed as well — such routes are created using Route.handled, whose decoded parameters include the trailing Path; then context = requestSegments - patternSegments - trailingSegments

    Both are exact, for a prefix of any length.

  • ZioHttpServerRequest: gains contextPathSegments(n), stored as an attribute, which pathSegments skips. uri remains the full request URI — the same split that the http4s interpreter already uses for routes mounted in a context.

Tests

ZioHttpCompositionTest covers a path capture with two methods, a fixed path, a paths wildcard and an endpoint without path inputs, all under .nest("api"), plus a check that un-prefixed paths still yield a 404. The test fails on master with 405 was not equal to 200.

zio-http's Routes.nest only prepends segments to the route pattern; the
Request passed to the handler keeps its full path. As the tapir interpreter
re-matches the whole path itself, none of the endpoints matched, resulting in
a 404 (or a 405, if the reject interceptor found a method mismatch first).

The interpreter now computes per-request how many leading path segments come
from the nesting prefix, and skips them when matching.
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.

[BUG] Method Not Allowed with zio-http server (nesting endpoints with path input)

1 participant