Skip to content

feat(endpoint): add renderUrl method to Endpoint (#3162)#4067

Open
987Nabil wants to merge 1 commit intozio:mainfrom
987Nabil:feat/endpoint-render-url
Open

feat(endpoint): add renderUrl method to Endpoint (#3162)#4067
987Nabil wants to merge 1 commit intozio:mainfrom
987Nabil:feat/endpoint-render-url

Conversation

@987Nabil
Copy link
Copy Markdown
Contributor

Summary

Adds Endpoint.renderUrl methods to render the URL template including both path and query parameter placeholders.

Usage

val ep = Endpoint(Method.GET / "api" / "users" / int("userId"))
  .query[Int](HttpCodec.query[Int]("page"))
  .query[Int](HttpCodec.query[Int]("limit"))

ep.renderUrl           // "/api/users/{userId}?page={page}&limit={limit}"
ep.renderUrl("/v1")    // "/v1/api/users/{userId}?page={page}&limit={limit}"

API

  • def renderUrl: String — renders path + query params
  • def renderUrl(basePath: String): String — with base path prefix

Query parameter names are extracted from the codec's record fields. Path parameters use the existing PathCodec.render format.

Closes #3162

@987Nabil 987Nabil requested a review from jdegoes as a code owner March 26, 2026 14:02
Copilot AI review requested due to automatic review settings March 26, 2026 14:02
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 26, 2026

Deploy Preview for zio-http ready!

Name Link
🔨 Latest commit d92875c
🔍 Latest deploy log https://app.netlify.com/projects/zio-http/deploys/69cce735a78b8a0008b7ec26
😎 Deploy Preview https://deploy-preview-4067--zio-http.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a convenience API on Endpoint to render a URL template string (path + query placeholders), intended for documentation / client generation / debugging use cases (per #3162).

Changes:

  • Introduces Endpoint.renderUrl: String to render the endpoint path template plus query parameter placeholders.
  • Introduces Endpoint.renderUrl(basePath: String): String to prefix a base path to the rendered template.
  • Adds a private queryParamNames extractor that walks the endpoint’s HttpCodec input to find query parameter names.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +213 to +218
def renderUrl: String = {
val pathPart = route.pathCodec.render
val queryPart = queryParamNames
if (queryPart.isEmpty) pathPart
else pathPart + "?" + queryPart.map(n => s"$n={$n}").mkString("&")
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

renderUrl / renderUrl(basePath) introduce new public behavior but there are no accompanying tests to lock in rendering for common cases (no query params, multiple individual query params, case-class query params, optional query params, fallbacks/alternatives, and basePath with/without trailing slash). Adding a focused spec would help prevent regressions and clarify intended output formatting.

Copilot uses AI. Check for mistakes.
case HttpCodec.Combine(left, right, _) => loop(left) ++ loop(right)
case HttpCodec.Annotated(codec, _) => loop(codec)
case HttpCodec.TransformOrFail(codec, _, _) => loop(codec)
case HttpCodec.Fallback(left, right, _, _) => loop(left) ++ loop(right)
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

HttpCodec.Fallback is currently handled by concatenating query param names from both sides (loop(left) ++ loop(right)), which can render duplicated placeholders (e.g. query(Int("userId") | query(String("userId")) would produce userId={userId}&userId={userId}) and can also imply that alternative query params are simultaneously present. Consider mirroring PathCodec.render behavior by taking only the left branch, or otherwise de-duplicating/encoding the fact that params are alternatives so the rendered URL is not misleading.

Suggested change
case HttpCodec.Fallback(left, right, _, _) => loop(left) ++ loop(right)
case HttpCodec.Fallback(left, right, _, _) => loop(left)

Copilot uses AI. Check for mistakes.
case HttpCodec.Empty => Chunk.empty
case q: HttpCodec.Query[_] =>
val fields = q.codec.recordFields.map(_._1.fieldName)
if (fields.nonEmpty) fields.map(_.toString)
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

q.codec.recordFields can contain null/empty field names when the query codec is derived without an explicit name (e.g. HttpCodec.query[Int] uses queryFromSchema(..., name = null) for primitives). In that case renderUrl will emit null={null}. It would be safer to filter out null/blank names and either omit them or fall back to a stable placeholder name.

Suggested change
if (fields.nonEmpty) fields.map(_.toString)
val validFields = fields.filter {
case null => false
case name => name.toString.trim.nonEmpty
}
if (validFields.nonEmpty) validFields.map(_.toString)

Copilot uses AI. Check for mistakes.
Comment on lines +225 to +228
def renderUrl(basePath: String): String = {
val base = if (basePath.endsWith("/")) basePath.dropRight(1) else basePath
base + renderUrl
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

renderUrl(basePath) strips exactly one trailing /, but other URL-building APIs in this type (e.g. url(basePath, values)) appear to do a raw concatenation. This difference can be surprising for callers and makes base-path semantics inconsistent. Consider aligning behavior (either normalize in both places or neither) and/or documenting what forms of basePath are supported (leading slash required? trailing slash ignored?).

Copilot uses AI. Check for mistakes.
@987Nabil 987Nabil force-pushed the feat/endpoint-render-url branch from 700c905 to d92875c Compare April 1, 2026 09:36
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.

Add method to render path with query parameters to Endpoint

2 participants