Skip to content
rhttp.io

Server Configuration

createServerHttp() is the factory for Node.js and SSR code. It wraps createHttp() with cookie forwarding, tracing, and metrics pre-enabled — and it auto-detects TanStack Start's request context out of the box.

import { createServerHttp } from "rhttp.io/server"; 
 
const http = createServerHttp({
  baseURL: "https://api.example.com",
  timeout: 30_000,
});
 
// Cookie forwarding, tracing, and prod metrics are already on.
const { data } = await http.get<User>("/protected-api");

Signature

function createServerHttp(config?: CreateHttpConfig): HttpClientInstance;

It accepts the same CreateHttpConfig shape as the core factory.

What it configures for you

The server factory sets auth.forwardCookies to true, which tells the core engine to copy the incoming request's Cookie header into every outbound request whenever a request context is available.

// Internally:
createHttp({
  ...config,
  auth: {
    forwardCookies: true, 
    ...config.auth,
  },
});

2. Observability — logger, tracing, metrics

Server requests are logged by default, traced with a unique x-request-id, and collect metrics in production.

// Internally:
createHttp({
  ...config,
  observability: {
    logger: true,
    tracing: true,
    metrics: process.env.NODE_ENV === "production", 
    ...config.observability,
  },
});

Beyond the forwardCookies flag, createServerHttp() registers a request interceptor that extracts cookies from the active request context and injects them into each outbound request's Cookie header. This works even without the async_hooks store.

The interceptor resolves the current request through two strategies, tried in priority order:

// Strategy 1: Explicit requestContext function from config.
if (config.requestContext && !request) {
  try {
    request = config.requestContext();
  } catch {
    // Ignore — not in request context yet.
  }
}
 
// Strategy 2: TanStack Start auto-detection (optional dependency).
if (!request) {
  try {
    const module = await import("@tanstack/react-start/server"); 
    if (module?.getRequest) {
      request = module.getRequest();
    }
  } catch {
    // TanStack Start not installed or not in a server function — skip.
  }
}
 
// Inject cookies into outbound request.
if (request && typeof request.headers?.get === "function") {
  const cookieHeader = request.headers.get("cookie");
  if (cookieHeader) {
    options.headers = options.headers || {};
    options.headers["cookie"] = cookieHeader; 
  }
}

TanStack Start integration

The simplest setup. TanStack Start is auto-detected — no explicit requestContext needed.

// src/lib/http.ts
import { createServerHttp } from "rhttp.io/server";
 
export const http = createServerHttp({
  baseURL: "https://api.example.com",
  timeout: 30_000,
});
// src/routes/users.tsx
import { createServerFn } from "@tanstack/react-start";
import { http } from "~/lib/http";
 
export const getUsers = createServerFn({ method: "GET" }).handler(async () => {
  const { data } = await http.get<User[]>("/users");
  return data;
});

When getUsers runs on the server, the interceptor dynamically imports @tanstack/react-start/server, calls getRequest(), and forwards the browser's cookies to your API — session cookies, CSRF tokens, everything.

Using withRequest for explicit context

For frameworks that use Node's async_hooks (or when you need to manually propagate the request), use withRequest():

import { createServerHttp } from "rhttp.io/server";
import { setRequestContextStore } from "rhttp.io/server";
import { AsyncLocalStorage } from "node:async_hooks";
 
const store = new AsyncLocalStorage();
setRequestContextStore(store); 
 
const http = createServerHttp({ baseURL: "https://api.example.com" });
 
// In your middleware / route handler:
app.get("/data", async (req, res) => {
  return store.run(req, async () => { 
    const { data } = await http.get("/protected");
    res.json(data);
  });
});

Express / generic Node.js

If you're not using TanStack Start, pass a requestContext function that returns the current request:

import { createServerHttp } from "rhttp.io/server";
 
const http = createServerHttp({
  baseURL: "https://api.example.com",
  requestContext: () => currentRequest, 
});

Or manage context with withRequest as shown above.

Production configuration

import { createServerHttp } from "rhttp.io/server";
 
export const http = createServerHttp({
  baseURL: process.env.INTERNAL_API_URL!,
  timeout: 30_000,
 
  // Resilience on unreliable internal networks.
  retry: { attempts: 3, strategy: "exponential", delay: 500, maxDelay: 10_000 },
  circuitBreaker: {
    enabled: true,
    failureThreshold: 10,
    successThreshold: 3,
    timeout: 60_000,
  },
  requestPool: { enabled: true, maxConcurrent: 10, queueLimit: 50 },
 
  // SSR usually needs fresh data — network-first is safe.
  cache: { enabled: false },
 
  // Observability (logger/tracing already on by default).
  observability: { metrics: true }, // already true in production
});

Summary of server defaults

OptionDefault valueCan override?
auth.forwardCookiestrue
observability.loggertrue
observability.tracingtrue
observability.metricsprocess.env.NODE_ENV === "production"
Cookie interceptorauto-registered
TanStack Start detectionauto (dynamic import)n/a