BBizKit

url.join

v0.1.0 latest

Join URL path segments with path-join semantics and optional query parameter merging per RFC 3986.

urlpath-joinquery-stringhttpapi-client

Join URL path segments with path-join semantics and optional query parameter merging per RFC 3986.

Signature

function join(base: string, ...parts: (string | Record<string, unknown>)[]): string

Problem

Building URLs from base paths and segments requires careful slash deduplication (base/ + /path → base/path, not base//path). Appending query parameters requires manual encoding and ? vs & selection. These patterns are repeated across every HTTP client call.

How It Works

Handles double-slash deduplication at join boundaries. The last Record argument is merged as query parameters using RFC 3986 encoding. null/undefined query values are skipped.

Boundaries

  • Uses path-join semantics, not RFC 3986 relative-reference resolution.
  • Record argument must be the last argument; placing it elsewhere throws TypeError.
  • Does not normalize the resulting URL (no percent-decoding, no WHATWG URL parsing).
  • Throws TypeError for non-string base.

Replaces

Common boilerplate this function replaces:

base + '/' + path + '?' + new URLSearchParams(query).toString()

Examples

url.join("https://api.example.com/", "/v1/", "users");  // "https://api.example.com/v1/users"
url.join("https://api.example.com", "search", { q: "hello", page: "1" });  // "https://api.example.com/search?q=hello&page=1"
url.join("/base/", "/path/");  // "/base/path/"
url.join("https://example.com/api", { page: "1" });  // "https://example.com/api?page=1"

Standards

Caveats

  • This is path-join, not RFC 3986 relative reference resolution. For spec-compliant resolution, use the URL constructor.
  • Query parameters are encoded using RFC 3986 with skipNull: true.

FAQ

How to join URL path segments in JavaScript?

url.join('https://api.com/', '/v1/', 'users') returns 'https://api.com/v1/users'. Handles slash deduplication automatically.

How to append query parameters to a URL?

Pass a Record as the last argument: url.join('https://api.com', 'search', { q: 'hello' }) returns 'https://api.com/search?q=hello'.

Does url.join handle double slashes?

Yes. url.join('https://api.com/', '/path') deduplicates to 'https://api.com/path'.

Can I use url.join for relative paths?

Yes. url.join('/base/', '/path/') returns '/base/path/'. It uses path-join semantics.

How are null query values handled?

null and undefined values are skipped. url.join('/api', { a: '1', b: null }) produces '/api?a=1'.