Skip to content

feat!: allow passing a custom HTTP client to ApifyClient - #1058

Merged
vdusek merged 29 commits into
v3from
feat/pluggable-http-client
Sep 25, 2026
Merged

vdusek merged 29 commits into
v3from
feat/pluggable-http-client

Conversation

@vdusek

@vdusek vdusek commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

ApifyClient sends its requests through an HTTP client you can replace, following apify-client-python (apify/apify-client-python#641). The abstract HttpClient holds the shared pipeline: header merging, body serialization, compression, retries with backoff, timeout growth, body parsing and the ApifyApiError conversion. A transport implements sendRequest() and can override isTimeoutError(), isRetryableTransportError() and close(). AxiosHttpClient is the only built-in transport, and async-retry is no longer a dependency.

ApifyClient.withCustomHttpClient({ token, baseUrl, publicBaseUrl, httpClient }) plugs a client in, as Python's with_custom_http_client does, since the retry and timeout options of the constructor configure the default client only. ApifyClientOptions.headers replaces the fixed-header use of requestInterceptors. ApifyRequestConfig.signal aborts a call, which then rejects with the signal's reason. HttpClient validates its retry and timeout options with the shape ApifyClientOptions uses.

Docs: an "HTTP clients" concept page, a "Build a custom HTTP client" guide over fetch with a section on reusing a Crawlee 4 client, the v3 upgrading guide and the regenerated api-extractor report. Tests drive a call()-only client and a hooks-only client over node:http through the pipeline.

Issue

Closes #855

Follow-up: #1071

Breaking changes

  • requestInterceptors moved from ApifyClientOptions to AxiosHttpClientOptions. Use headers for a fixed header, or pass an AxiosHttpClient to withCustomHttpClient(). An interceptor now sees the body already serialized.
  • ApifyRequestConfig and ApifyResponse no longer extend the axios types. forceBuffer became responseType: 'buffer', and responseType takes 'parsed' | 'buffer' | 'stream'.
  • InvalidResponseBodyError.response is the transport's HttpResponse with the raw body.
  • ApifyApiError.httpMethod is uppercase (GET).
  • A Blob, File, FormData or web ReadableStream body throws a TypeError. Read it into a Buffer first.
  • A form-encoded object body is encoded by the client: binary values are base64-encoded and nested arrays use key[].
  • The error thrown after the retries are exhausted is the last one, as in Python.
  • The per-attempt timeout doubles on a fixed schedule up to timeoutMaxSecs, and retry delays are randomized.
  • The axios, httpAgent and httpsAgent properties moved from HttpClient to AxiosHttpClient, and the instance no longer carries the default headers.

✍️ Drafted by Claude Code

@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Sep 10, 2026
@vdusek vdusek self-assigned this Sep 10, 2026
@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ There are broken links in the documentation.

See more at https://github.com/apify/apify-client-js/actions/runs/36108249341#summary-107985616997

# Conflicts:
#	docs/02_concepts/02_error-handling.md
#	docs/public-api/apify-client.api.md
#	src/apify_api_error.ts
#	src/apify_client.ts
#	src/base/resource_client.ts
#	src/http_client.ts
#	src/resource_clients/actor.ts
#	src/resource_clients/dataset.ts
#	src/resource_clients/key_value_store.ts
#	src/resource_clients/log.ts
#	src/resource_clients/request_queue.ts
#	src/resource_clients/run.ts
#	src/resource_clients/task.ts
#	test/client_timeouts.test.ts
#	test/http_client.test.ts
# Conflicts:
#	docs/04_upgrading/upgrading_v3.md
#	src/apify_api_error.ts
#	src/http_client.ts
#	src/resource_clients/dataset.ts
# Conflicts:
#	docs/04_upgrading/upgrading_v3.md
#	src/apify_api_error.ts
#	src/http_client.ts
#	src/interceptors.ts
#	src/utils.ts
#	test/http_client.test.ts
The configurable request compression from #1065 moves into the shared
pipeline of the new `HttpClient` base, where the compression step lives
now that `http_client.ts` and `interceptors.ts` are gone. `compression`
becomes an `HttpClientOptions` field resolved in the base constructor, so
`ApifyClient` forwards the option instead of resolving it, and a custom
HTTP client gets the same compression as `AxiosHttpClient`.

The new HTTP clients concept page moves to `08_http-clients.md`, since
#1065 took the `07_` slot for HTTP compression.
@vdusek
vdusek marked this pull request as ready for review September 16, 2026 08:40
@vdusek
vdusek requested a review from szaganek as a code owner September 16, 2026 08:40
@vdusek
vdusek requested a review from janbuchar September 16, 2026 08:40
Comment thread src/http_clients/base.ts Outdated
Comment thread src/apify_client.ts Outdated
Comment thread src/apify_client.ts
Comment thread docs/03_guides/02_custom-http-client.md Outdated
Comment thread src/apify_api_error.ts Outdated
Comment thread src/http_clients/base.ts
Comment thread src/apify_client.ts
@vdusek
vdusek requested a review from janbuchar September 16, 2026 13:54
Comment thread src/http_clients/base.ts
Comment thread src/apify_api_error.ts
Comment thread docs/04_upgrading/upgrading_v3.md Outdated
@vdusek
vdusek merged commit a95275f into v3 Sep 25, 2026
8 checks passed
@vdusek
vdusek deleted the feat/pluggable-http-client branch September 25, 2026 07:36
vdusek added a commit that referenced this pull request Sep 25, 2026
Adds a regression test for #277: `start()` with a `Buffer` input and
`contentType: 'application/json'` sends the raw bytes, and the server
gets the decoded JSON back.

The bug itself is already gone on `v3`. `serializeBody()` from #1058
passes binary and string bodies through before it reaches
`JSON.stringify`, and axios no longer re-serializes strings since #1051
set `transformRequest: []`. With the Buffer branch disabled, the new
test fails with the `{ type: 'Buffer', data: [...] }` payload from the
issue.

Closes #277

*✍️ Drafted by Claude Code*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants