Skip to content

feat(http): auto User-Agent + custom-headers override#27

Merged
johnpmitsch merged 3 commits into
mainfrom
jm/user-agent-and-headers
May 27, 2026
Merged

feat(http): auto User-Agent + custom-headers override#27
johnpmitsch merged 3 commits into
mainfrom
jm/user-agent-and-headers

Conversation

@johnpmitsch

@johnpmitsch johnpmitsch commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Every outbound HTTP request now carries an auto-generated User-Agent of the form quicknode-sdk-<lang>/<sdk-version> (<os>-<arch>; <lang>-<runtime-version>). Each binding (Python, Node, Ruby) supplies its own ClientInfo so the user agent identifies the package the caller actually installed — npm 3.x.y, PyPI a27, gem alpha.27, etc. — not the underlying Rust core.
  • HttpConfig gains an optional headers map. Caller-supplied headers override every SDK-managed header (User-Agent, x-api-key, Accept, Content-Type); docblocks call this out explicitly. Maximum flexibility — trust the caller.
  • Ruby additionally gets QuicknodeSdk::SDK.from_config(opts) so it matches the Python/Node explicit-config surface and can set headers without env vars.

these are the exact strings for default user-agent header the SDK will use on Apple Silicon macOS (darwin/aarch64), built against the current 0.1.0-alpha.27 train. The is the language-package version,
not the Rust core version.

  - Rust
  quicknode-sdk-rust/0.1.0-alpha.27 (macos-aarch64; rust-1.85)
  (rustc version comes from CARGO_PKG_RUST_VERSION — the MSRV. Falls back to unknown if unset.)
  - Python (CPython 3.12.4 on macOS arm64)
  quicknode-sdk-python/0.1.0a27 (macos-aarch64; python-3.12.4)
  (Python version reads sys.version_info; SDK version reads importlib.metadata.version("quicknode-sdk") — PyPI uses PEP 440, so 0.1.0-alpha.27 → 0.1.0a27.)
  - Node (Node 20.10.0 on macOS arm64)
  quicknode-sdk-node/3.1.0-alpha.27 (macos-aarch64; node-20.10.0)
  (Node version reads process.version, stripping the leading v. SDK version is 3.x.y because @quicknode/sdk 2.x already shipped on npm — release-bump translates 0.x.y → 3.x.y.)
  - Ruby (Ruby 3.3.0 on macOS arm64)
  quicknode-sdk-ruby/0.1.0-alpha.27 (macos-aarch64; ruby-3.3.0)
  (Ruby version reads RUBY_VERSION; SDK version comes from s.version in the gemspec.)

On Linux x86_64 the platform segment changes to linux-x86_64; on aarch64 Linux it's linux-aarch64. Those values come from std::env::consts::{OS, ARCH} in the core crate, so the binding language
doesn't affect them.

Custom header override examples

Rust

use std::collections::HashMap;
use quicknode_sdk::{HttpConfig, QuicknodeSdk, SdkFullConfig};

let mut headers = HashMap::new();
headers.insert("X-Correlation-Id".to_string(), "abc-123".to_string());
headers.insert("User-Agent".to_string(), "my-app/1.0".to_string()); // overrides SDK default

let qn = QuicknodeSdk::new(
    &SdkFullConfig::builder()
        .api_key("your-key")
        .http(HttpConfig { headers: Some(headers), ..Default::default() })
        .build(),
)?;

Python

from sdk import QuicknodeSdk, SdkFullConfig, HttpConfig

qn = QuicknodeSdk(
    SdkFullConfig(
        api_key="your-key",
        http=HttpConfig(headers={
            "X-Correlation-Id": "abc-123",
            "User-Agent": "my-app/1.0",  # overrides SDK default
        }),
    )
)

Node

import { QuicknodeSdk } from "@quicknode/sdk";

const qn = new QuicknodeSdk({
  apiKey: "your-key",
  http: {
    headers: {
      "X-Correlation-Id": "abc-123",
      "User-Agent": "my-app/1.0", // overrides SDK default
    },
  },
});

Ruby

qn = QuicknodeSdk::SDK.from_config(
  api_key: "your-key",
  http: {
    headers: {
      "X-Correlation-Id" => "abc-123",
      "User-Agent" => "my-app/1.0", # overrides SDK default
    },
  },
)

Fixes DX-5042
Fixes DX-5302

Test plan

  • cargo check && just lint && just test (203 unit tests pass; new tests cover default UA shape, language-binding UA shape, custom-header overrides, invalid header name/value, and a wiremock test asserting both UA and custom headers reach the wire)
  • just python-build (stubs regenerated with the new headers parameter)
  • just node-build (index.d.ts regenerated with the new headers field; existing test suite passes)
  • just ruby-build (smoke-tested QuicknodeSdk::SDK.from_config(api_key:, http: { headers: ... }) constructs cleanly)
  • Run each language's e2e example end-to-end against the live API with QN_API_KEY set:
    • cargo run --example admin_e2e -p quicknode-sdk --features rust
    • uv run python/examples/admin.py
    • cd npm && npx tsx examples/admin.ts
    • ruby ruby/examples/admin_e2e.rb
  • Verify the server-side telemetry shows the new User-Agent shape after a release.

Every outbound HTTP request now carries an auto-generated User-Agent
identifying the language, package version, and platform. Each binding
crate (Python, Node, Ruby) supplies its own ClientInfo so the User-Agent
matches the package the caller installed, not the underlying Rust core.

HttpConfig also gains an optional headers map. Caller-supplied headers
override every SDK-managed header (User-Agent, x-api-key, Accept,
Content-Type) — docblocks call this out explicitly.

Ruby additionally gets QuicknodeSdk::SDK.from_config so it matches the
Python/Node explicit-config surface and can set headers without going
through env vars.
@johnpmitsch johnpmitsch merged commit 3f11954 into main May 27, 2026
4 checks passed
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.

2 participants