@qfetch/middleware-base-url
v0.0.0-reserved.0
Published
Fetch middleware for base URL resolution with consistent same-origin handling.
Downloads
16
Maintainers
Readme
@qfetch/middleware-base-url
Fetch middleware for automatically resolving URLs against a configured base URL.
Overview
Automatically resolves request URLs against a configured base URL with consistent same-origin handling. All same-origin requests (even those with absolute paths like /users) are treated as relative to the base path, while different-origin requests pass through unchanged.
This utility-first approach deviates from strict URL Standard behavior to provide a more intuitive and consistent developer experience when working with API clients.
Intended for use with the composable middleware system provided by @qfetch/core.
Installation
npm install @qfetch/middleware-base-urlAPI
withBaseUrl(options)
Creates a middleware that resolves request URLs against the given base URL.
Options
- Base URL (
string | URL) (required) - The base URL to resolve requests against- Accepts either a string or URL instance
- Must be a valid URL (throws
TypeErrorif invalid) - Trailing slash recommended for predictable path resolution
- Example:
"https://api.example.com/v1/"ornew URL("https://api.example.com/v1/")
Behavior
- Same-origin requests - All paths (including those starting with
/) are treated as relative and resolved against the base path - Different-origin requests - Passed through unchanged (cross-origin requests remain intact)
- Type preservation - Input types are preserved (string→string, URL→URL, Request→Request)
- Query parameters and fragments - Always preserved during URL resolution
- Request properties - Method, headers, body, and other properties are preserved when reconstructing Request objects
URL Resolution Behavior
The middleware uses consistent same-origin detection across all input types:
Same-Origin Requests
All same-origin requests (strings, URLs, or Requests) have their paths treated as relative and resolved against the base path - even if they start with /:
"users"→ appended to base path"/users"→ also appended to base path (leading slash stripped)new URL("/users", origin)→ pathname appended to base path
Different-Origin Requests
Cross-origin URLs are passed through unchanged, regardless of input type:
"https://example.com/data"→ unchangednew URL("https://example.com/data")→ unchanged
This consistent behavior favors practical utility: if you're using a base URL middleware, you probably want all same-origin requests to use that base path.
Important Note: Trailing Slashes
A trailing slash (/) is recommended at the end of the base URL.
Without it, the URL constructor treats the final path segment as a filename and replaces it instead of appending new paths. This follows standard URL resolution behavior:
new URL("users", "https://api.example.com/v1"); // → "https://api.example.com/users"
new URL("users", "https://api.example.com/v1/"); // → "https://api.example.com/v1/users"Usage
Basic Usage with String Inputs
import { withBaseUrl } from '@qfetch/middleware-base-url';
import { compose } from '@qfetch/core';
// Create a fetch instance with a base URL
const qfetch = compose(
withBaseUrl('https://api.example.com/v1/')
)(fetch);
// Same-origin paths → all resolve against the base
await qfetch('users'); // → https://api.example.com/v1/users
await qfetch('/users'); // → https://api.example.com/v1/users (leading slash stripped)
// Different-origin URL → left unchanged
await qfetch('https://external.com/data'); // → https://external.com/dataUsing with URL Objects
URL objects with the same origin have their paths resolved against the base:
import { withBaseUrl } from '@qfetch/middleware-base-url';
const qfetch = withBaseUrl('https://api.example.com/v1/')(fetch);
// Same origin → path resolved against base
const sameOriginUrl = new URL('/users', 'https://api.example.com');
await qfetch(sameOriginUrl); // → https://api.example.com/v1/users
// Different origin → passed through unchanged
const differentOriginUrl = new URL('https://external.com/data');
await qfetch(differentOriginUrl); // → https://external.com/data
// Query parameters and hash are preserved
const urlWithQuery = new URL('/users?page=1#top', 'https://api.example.com');
await qfetch(urlWithQuery); // → https://api.example.com/v1/users?page=1#topUsing with Request Objects
Request objects follow the same same-origin resolution logic as URL objects:
import { withBaseUrl } from '@qfetch/middleware-base-url';
const qfetch = withBaseUrl('https://api.example.com/v1/')(fetch);
// Same-origin Request → path resolved against base
const sameOriginRequest = new Request(
new URL('/users', 'https://api.example.com'),
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'John Doe' })
}
);
// → https://api.example.com/v1/users
// All other properties (method, headers, body) are preserved
await qfetch(sameOriginRequest);
// Different-origin Request → passed through unchanged
const crossOriginRequest = new Request('https://external.com/webhook', {
method: 'POST',
body: JSON.stringify({ event: 'user.created' })
});
await qfetch(crossOriginRequest); // → https://external.com/webhookImportant Limitations
Request Object Reconstruction
Requestobjects are immutable according to the Fetch API specification. When resolving same-origin requests, newRequestobjects are created with the resolved URL. All request properties (method, headers, body, etc.) are preserved during reconstruction.
Request Body Handling Request body streams are preserved but not cloned. The body remains consumable exactly once. For requests with non-replayable body types (like
ReadableStream), the body will be consumed during the first attempt and cannot be retried without providing a fresh body stream.
Notes
- The middleware preserves input types (string→string, URL→URL, Request→Request) throughout the middleware chain
- Same-origin detection is based on URL origin comparison (protocol + host + port)
- Query parameters and URL fragments are always preserved during resolution
- Different-origin requests bypass base URL resolution entirely
- A trailing slash (
/) at the end of the base URL is recommended for predictable path resolution
Standards References
- WHATWG URL Standard - Defines URL resolution behavior
- MDN: URL API - Browser implementation documentation
- Fetch Standard - Defines Request and fetch API semantics
