Apify SDK for JavaScript/TypeScript

repository·master·Indexed 41 days ago

https://github.com/apify/apify-sdk-js

The official toolkit for building Apify Actors in JavaScript and TypeScript. It provides utilities for managing the Actor lifecycle (init, exit, main), accessing platform storage (Key-Value Store, Datasets), handling platform events, and configuring proxies. Version 3.7.2 enables the development of data extraction and web automation jobs, integrating with Crawlee and browser automation tools like Playwright and Puppeteer.

Tokens
46.5K
Snippets
163
Records
197
Agent score
50%

What's inside Apify SDK

  1. Overview of Apify Docker images

    master

    Apify provides base Docker images to simplify running headless browsers in Docker. Images are available in two flavors:

    1. Full images: Preinstall apify, crawlee, and typescript. Use these for quick execution without maintaining a package.json or when you want to rely on specific preinstalled versions.
    2. -slim variants: Only ship the browser automation library (e.g., puppeteer, playwright, or camoufox-js) or no npm packages at all (e.g., apify/actor-node:24-slim). These are smaller and faster to pull. Your package.json serves as the single source of truth for dependencies.

    Available Image Families:

    • apify/actor-node
    • apify/actor-node-puppeteer-chrome
    • apify/actor-node-playwright (includes chrome, firefox, webkit, and camoufox variants)

    Recommendation: Use the -slim variant unless you have a specific reason not to.

    # No preinstalled packages, bring your own dependencies.
    FROM apify/actor-node:24-slim
  2. What is SessionPool and why use it?

    master

    The SessionPool class manages the rotation of proxy IP addresses along with cookies and other custom settings. It provides three main benefits:

    1. Filtering blocked proxies: It allows you to identify and exclude blocked or non-working proxies so your actor doesn't waste retries on them.
    2. Session persistence: You can tie specific identifiers (like cookies, auth tokens, or particular headers) to a specific IP address. Using the same cookies with the same IP reduces the risk of being flagged as suspicious.
    3. Even IP rotation: It picks sessions randomly to prevent burning out a small subset of available IPs.

    Note: A SessionPool requires time to find working IPs and stabilize; expect initial errors during the startup phase.

  3. What is an Actor?

    master
    An Actor is a serverless microservice deployed to the Apify platform. It accepts an input and produces an output. Actors can run for varying durations (seconds to infinity) and can perform tasks ranging from simple form filling to complex web crawling. When you share an actor in the Apify Store, it runs under the user's account, not yours.
  4. Use BrowserPool and BrowserController for browser management

    master

    SDK v1 replaces PuppeteerPool with BrowserPool to support both Puppeteer and Playwright. BrowserPool manages the lifecycle of browsers and provides several key features:

    Accessing the BrowserPool

    You can access the browserPool instance on the crawler object in both PuppeteerCrawler and PlaywrightCrawler.

    Using BrowserController

    The BrowserController is a class provided within the crawling context that offers a unified API for both Puppeteer and Playwright.

    Important: To ensure graceful shutdowns and compatibility across both libraries, use browserController instead of calling page.browser().close() or using library-specific methods like page.setCookies().

    • To close a browser: Use await browserController.close().
    • To set cookies: Use await browserController.setCookies(page, cookies).

    Lifecycle Hooks

    You can configure lifecycle hooks via browserPoolOptions in your crawler configuration. This allows you to run code (like preLaunchHooks) before a browser is launched, using the pageId to retrieve the relevant context.

    const crawler = new Apify.PuppeteerCrawler({
        browserPoolOptions: {
            retireBrowserAfterPageCount: 10,
            preLaunchHooks: [
                async (pageId, launchContext) => {
                    const { request } = crawler.crawlingContexts.get(pageId);
                    if (request.userData.useHeadful === true) {
                        launchContext.launchOptions.headless = false;
                    }
                },
            ],
        },
    });
    
    const handlePageFunction = async ({ page, browserController }) => {
        // Correct usage for graceful shutdown and cross-library compatibility
        await browserController.close();
        
        const cookies = [{ name: 'example', value: '123' }];
        await browserController.setCookies(page, cookies);
    };
  5. Use Key-Value Stores for files and state

    master

    Key-value stores are used for saving and reading data records or files (e.g., screenshots, PDFs, or crawler state). Each record is identified by a unique key and a MIME content type.

    Default Key-Value Store

    Each actor run has a default key-value store. By convention:

    • Actor input is stored under the INPUT key.
    • Actor output is stored under the OUTPUT key.

    Accessing Key-Value Stores

    You can use the KeyValueStore class or the convenience methods on the Actor class:

    • Actor.getValue(key): Retrieve a value.
    • Actor.setValue(key, value): Save a value.
    • Actor.openKeyValueStore(name): Open a specific named store.

    Local Storage Structure

    In local development, files are stored at: {APIFY_LOCAL_STORAGE_DIR}/key_value_stores/{STORE_ID}/{KEY}.{EXT}

    Note: The default store ID is default, but can be overridden using the APIFY_DEFAULT_KEY_VALUE_STORE_ID environment variable.

    // Get actor input from the default key-value store
    const input = await Actor.getInput();
    
    // Write actor output to the default key-value store.
    await Actor.setValue('OUTPUT', { myResult: 123 });
    
    // Open a named key-value store
    const store = await Actor.openKeyValueStore('some-name');
    
    // Write record. JavaScript objects are automatically converted to JSON.
    await store.setValue('some-key', { foo: 'bar' });
    
    // Read record. JSON is automatically parsed; text is returned as string; others as binary buffer.
    const value = await store.getValue('some-key');
    
    // Delete record
    await store.setValue('some-key', null);
  6. Handle graceful shutdowns in Actors

    master

    Actors on the Apify platform may receive signals to shut down (e.g., when a user aborts a run or the platform migrates the actor to a different server).

    • aborting event: The SDK calls Actor.exit(), which waits for cleanup handlers before exiting.
    • migrating event: The SDK calls Actor.reboot(), which waits for cleanup handlers and then restarts the Actor on a new worker.

    You can register cleanup logic using the persistState event. You can also configure a delay before shutdown via gracefulShutdownDelayMillis in Actor.init() or disable automatic handling by setting gracefulShutdown: false.

    import { Actor } from 'apify';
    
    await Actor.init({
        gracefulShutdownDelayMillis: 5000, // Wait 5 seconds before exiting/rebooting
    });
    
    // Register cleanup handler — runs on both exit and reboot
    Actor.on('persistState', async () => {
        console.log('Saving state...');
        await Actor.setValue('STATE', myState);
    });
    
    // Your Actor logic here
    
    await Actor.exit();
  7. Manage proxies with ProxyConfiguration

    master

    All proxy management in the Apify SDK is handled via the ProxyConfiguration class. You can create an instance using Actor.createProxyConfiguration(). This class supports both Apify Proxy (a super-proxy service) and your own custom proxy URLs.

    To use Apify Proxy, ensure your scraper is logged in (via the Actor lifecycle).

    // Using Apify Proxy
    const proxyConfiguration = await Actor.createProxyConfiguration();
    const proxyUrl = proxyConfiguration.newUrl();
    
    // Using your own custom proxies
    const proxyConfiguration = await Actor.createProxyConfiguration({
        proxyUrls: ['http://proxy-1.com', 'http://proxy-2.com'],
    });
    const proxyUrl = proxyConfiguration.newUrl();
  8. Difference between Apify Proxy and custom proxies

    master

    The ProxyConfiguration class supports two distinct modes depending on how you instantiate it:

    1. Apify Proxy Mode: Created via Actor.createProxyConfiguration(). This uses Apify's API-driven proxy system. Most configuration options (like groups and countryCode) are specific to this mode.
    2. Custom Proxy Mode: Created via the ProxyConfiguration constructor. This is used for third-party proxy providers. You provide specific URLs via proxyUrls and a custom newUrlFunction to manage them.
  9. Use RequestQueue for deep crawling

    master

    A RequestQueue is a storage of URLs used for deep crawling, where you start with initial URLs and recursively follow links to discover new ones. It supports both breadth-first and depth-first crawling orders.

    Key characteristics:

    • Dynamic: You can add or remove requests during the run.
    • Default Queue: Each actor run has a default request queue with the ID default. You can override this ID by setting the APIFY_DEFAULT_REQUEST_QUEUE_ID environment variable.
    • Implementation: In local development, it is emulated using a SQLite database located at {APIFY_LOCAL_STORAGE_DIR}/request_queues/{QUEUE_ID}/db.sqlite.

    Use the RequestQueue class to manage these URLs.

    // Open the default request queue associated with the actor run
    const requestQueue = await RequestQueue.open();
    
    // Enqueue the initial request
    await requestQueue.addRequest({ url: 'https://example.com' });
    
    // The crawler will automatically process requests from the queue
    const crawler = new CheerioCrawler({
        requestQueue,
        handlePageFunction: async ({ $, request }) => {
            // Add new request to the queue
            await requestQueue.addRequest({ url: 'https://example.com/new-page' });
            // Add links found on page to the queue
            await Actor.utils.enqueueLinks({ $, requestQueue });
        },
    });
  10. Understand the split between Crawlee and Apify SDK

    master

    Starting with version 3, the project has been split into two distinct parts to separate web-scraping logic from platform-specific helpers:

    • Crawlee: The web-scraping library. Available as the crawlee package on NPM.
    • Apify SDK: Helpers specifically for the Apify platform. Available as the apify package on NPM.

    If you are looking for crawler implementations (like PlaywrightCrawler or CheerioCrawler), you should now use crawlee instead of apify.

  11. How Crawling Context works in SDK v1

    master

    In SDK v1, handler arguments (like handlePageFunction or handleFailedRequestFunction) no longer receive separate, unique objects for every invocation. Instead, they all receive a single, shared object called the Crawling Context.

    This change allows you to track values across different function invocations because the context object is consistent. You can identify specific contexts using the id property and access other running contexts via the crawler.crawlingContexts Map.

    let masterContextId;
    const handlePageFunction = async ({ id, page, request, crawler }) => {
        if (request.userData.masterPage) {
            masterContextId = id;
            // Prepare the master page.
        } else {
            // Access a different context using its ID
            const masterContext = crawler.crawlingContexts.get(masterContextId);
            const masterPage = masterContext.page;
            const masterRequest = masterContext.request;
        }
    };