Skip to content

URL composition: prefix vs baseURL — pick one and document precedence #29

@productdevbook

Description

@productdevbook

Why

ky has both prefix and baseUrl. They interact (prefix is joined with slashes, then baseUrl is resolved). Users get confused. We should make a deliberate, documented choice before v0.1 ships.

Evidence

  • ky source/types/options.ts:142, 168, source/core/Ky.ts:376, 389-399.
  • ofetch has just baseURL and withBase() (src/utils.url.ts:40-51) — simpler, but vulnerable to a known SSRF prefix attack (ofetch #568).
  • redaxios has baseURL only (line 187).

Decision needed

Option A: baseURL only (ofetch / axios style)

baseURL: 'https://api.example.com'
misina.get('users/42')        // → https://api.example.com/users/42
misina.get('/users/42')       // → https://api.example.com/users/42
misina.get('https://other')   // → https://other (absolute wins)

Pros: simplest. Cons: less expressive when version-prefixing.

Option B: baseURL + prefix (ky style)

baseURL: 'https://api.example.com', prefix: '/v2'
misina.get('users/42')        // → https://api.example.com/v2/users/42

Pros: clean version bumps via .extend({ prefix: '/v3' }). Cons: extra concept.

Option C: drop both, just take URL

Force users to pass a URL object built however. Cons: too rigid for the 80% case.

Open questions

  1. Absolute URL precedence — does an absolute URL always override baseURL? axios has allowAbsoluteUrls: false to forbid this (security). We probably want allowAbsoluteUrls: true (default) for ergonomics, but expose the flag.
  2. SSRF prefix attack — if baseURL: 'http://api.internal', an attacker passing http://api.internal.attacker.com should fail because hostname differs. Use URL parsing + hostname check, never startsWith (the ofetch bug).
  3. Slash normalization — collapse // between baseURL and path; preserve ? query.

Proposal

Recommend Option A (baseURL only) for v0.1 — simpler, matches ofetch/axios. Add prefix later if real demand surfaces. Always parse with new URL(input, baseURL) — never string concat.

allowAbsoluteUrls: boolean defaults to true but is exposed for security-sensitive deployments.

Refs: ky source/core/Ky.ts:389-399, ofetch src/utils.url.ts:40-51, ofetch issue #568, axios lib/core/buildFullPath.js.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Critical / blocking core designcoreCore fetch / runtimedesignDesign decision required

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions