Skip to content

feat: implement crawlee v4 RequestQueueClient over apify-client - #643

Open
B4nan wants to merge 2 commits into
v4from
feat/crawlee-v4-request-queue
Open

feat: implement crawlee v4 RequestQueueClient over apify-client#643
B4nan wants to merge 2 commits into
v4from
feat/crawlee-v4-request-queue

Conversation

@B4nan

@B4nan B4nan commented Jun 16, 2026

Copy link
Copy Markdown
Member

Crawlee v4 (apify/crawlee#3729) redesigned RequestQueueClient into a stateful, pull-based interface (modeled on the Python SDK) — addBatchOfRequests, fetchNextRequest, markRequestAsHandled, reclaimRequest, isEmpty, isFinished — replacing the old thin REST wrapper. The SDK's old name-remapping proxy can't satisfy it (addBatchOfRequests is not a function at runtime), so this adds a real implementation.

Changes

  • ApifyRequestQueueClient (new file) — implements the new interface on apify-client's REST API:
    • fetchNextRequest locks the head server-side (listAndLockHead) so the queue is safe to share across consumers; markRequestAsHandled / reclaimRequest update the request and release its lock (deleteRequestLock).
    • request ids are derived from the unique key the same way as the platform (sha256 → base64 → strip → 15 chars).
    • isEmpty via listHead; isFinished via pendingRequestCount (counts in-progress, so 0 ⇒ truly done).
  • ApifyStorageClient.setStatusMessage — Crawlee v4 calls this on the storage client to surface crawl progress (it was warning setStatusMessage is not a function).
  • Bumps @crawlee/* to ^4.0.0-beta.66; removes the now-dead initialCount hack.

Validation

Verified end-to-end on the Apify platform: a CheerioCrawler run through Apify Proxy completed 29/29 requests, 0 failed, driven entirely by the new request-queue client (addBatchOfRequestsfetchNextRequestmarkRequestAsHandledisEmpty/isFinished), with setStatusMessage working and no warnings. Unit tests (123) + build + lint clean.

⚠️ Note on published crawlee@beta.66

Published @crawlee/types@beta.66 still ships the pre-redesign interface in its .d.ts, even though @crawlee/core@beta.66's runtime already calls the new methods (and @crawlee/memory-storage@beta.66 doesn't implement them — so local MemoryStorage crawls are broken on published 66). beta.66 is effectively a broken transitional release. This PR targets the new interface (correct for the runtime, validated on-platform) and implements a local copy of it (RequestQueueClientV4) until crawlee republishes regenerated types — at which point that copy is deleted and we implements @crawlee/types' RequestQueueClient directly.

🤖 Generated with Claude Code

Crawlee v4 (apify/crawlee#3729) redesigned `RequestQueueClient` into a stateful,
pull-based interface (`addBatchOfRequests`, `fetchNextRequest`, `markRequestAsHandled`,
`reclaimRequest`, `isEmpty`, `isFinished`) modeled on the Python SDK — replacing the
old thin REST wrapper. The old name-remapping proxy can't satisfy it, so this adds a
real `ApifyRequestQueueClient` implementing the interface on apify-client's REST API:

- `fetchNextRequest` locks the head server-side (`listAndLockHead`) so the queue is
  safe to share across consumers; `markRequestAsHandled` / `reclaimRequest` update the
  request and release the lock.
- request ids are derived from the unique key the same way as the platform.
- `getMetadata`/`drop`, no-op `purge`.

Also adds `ApifyStorageClient.setStatusMessage` (Crawlee v4 calls it on the storage
client to surface crawl progress) and bumps `@crawlee/*` to `^4.0.0-beta.66`.

Validated end-to-end on the Apify platform: a CheerioCrawler run through Apify Proxy
completed 29/29 requests driven entirely by the new request-queue client.

Note: published `@crawlee/types@beta.66` still ships the pre-redesign interface in its
`.d.ts` even though `@crawlee/core`'s runtime calls the new methods, so the client
implements a local copy of the new interface (`RequestQueueClientV4`) until crawlee
republishes regenerated types.
@B4nan B4nan added the adhoc Ad-hoc unplanned task added during the sprint. label Jun 16, 2026
@B4nan
B4nan requested a review from janbuchar June 16, 2026 13:21
Crawlee v4 (apify/crawlee#3740) renamed the storage abstraction — `StorageClient`
→ `StorageBackend`, `RequestQueueClient` → `RequestQueueBackend`, `createXClient`
→ `createXBackend` — moved the in-memory client into `@crawlee/core`
(`MemoryStorageBackend`, dropping the `@crawlee/memory-storage` package), and
further refined the request-queue signatures (`fetchNextRequest`/`getRequest` now
return `UpdateRequestSchema | undefined`, `null` → `undefined`, async
`setExpectedRequestProcessingTimeSecs`).

- `ApifyStorageClient` now implements `StorageBackend` (`createDatasetBackend` /
  `createKeyValueStoreBackend` / `createRequestQueueBackend`).
- `ApifyRequestQueueClient` implements the real `RequestQueueBackend` directly —
  the temporary local interface copy is gone.
- `StorageBackend` also dropped `setStatusMessage`, so `Actor.setStatusMessage`
  now sets the run status directly via apify-client.
- Bumps `@crawlee/*` to `^4.0.0-beta.71`.

Compile, 123 unit tests, and a local (memory-backend) smoke run on crawlee@71 all
pass. The request-queue logic itself was validated end-to-end on the Apify platform
against crawlee@66; on-platform validation on @71 is currently blocked by a crawlee
packaging gap (its `@crawlee/fs-storage` native binding is unpublished for musl).

@janbuchar janbuchar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description seems outdated, e.g.:

  • ApifyStorageClient.setStatusMessage — Crawlee v4 calls this on the storage client to surface crawl progress (it was warning setStatusMessage is not a function).

is no longer true since apify/crawlee#3818

Comment thread src/storage.ts
export interface OpenStorageContext {
config: Configuration;
client?: StorageClient;
client?: StorageBackend;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the field deserves a rename too?

Comment on lines +210 to +211
// Crawlee v4's RequestQueueBackend is a stateful, pull-based interface that
// can't be satisfied by name-remapping; it's implemented on apify-client.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Crawlee v4's RequestQueueBackend is a stateful, pull-based interface that
// can't be satisfied by name-remapping; it's implemented on apify-client.

ok thx bye

* ```
*/
export class ApifyStorageClient implements StorageClient {
export class ApifyStorageClient implements StorageBackend {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename the class and file please

}

async createRequestQueueClient(options?: CreateRequestQueueClientOptions): Promise<RequestQueueClient> {
async createRequestQueueBackend(options?: CreateRequestQueueBackendOptions): Promise<RequestQueueBackend> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The options don't make it possible to toggle between single and shared modes (no locking vs locking) — is this tracked in an issue? Or shall we fix it right here right now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adhoc Ad-hoc unplanned task added during the sprint.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants