Overview

9 Extending Airflow with custom operators and sensors

This chapter shows how to make Airflow DAGs more modular, concise, and reusable by moving task-specific logic into custom hooks, operators, and sensors, and by packaging those components for distribution. It begins with a simple movie-recommendation use case implemented via PythonOperator and demonstrates why ad hoc code (API sessions, auth, pagination, file I/O) quickly becomes boilerplate that is hard to reuse. The chapter’s overarching goal is to encapsulate that logic in clean, testable components that integrate smoothly with Airflow’s connection management, templating, and scheduling, and to introduce deferrable sensors/operators to conserve worker resources during long waits.

The core implementation centers on a MovielensHook that subclasses BaseHook to manage an authenticated requests session, build the base URL from an Airflow Connection, and handle pagination, with simple caching to avoid repeated metastore lookups. Building on the hook, a MovielensFetchRatingsOperator (BaseOperator) implements execute to fetch ratings and write them to storage, exposes start/end dates and output path as template fields, and uses the operator’s logger and safe resource cleanup. For orchestration, a MovielensRatingsSensor (BaseSensorOperator) polls for data availability via a lightweight next() check on the hook’s generator, while a deferrable variant offloads the wait to Airflow’s Triggerer using an asyncio-based trigger, freeing workers and improving scalability; the chapter contrasts embedding “deferrable” logic in one class versus using a separate Async class and adopts the latter for clarity.

Finally, the chapter packages these components into a small Python distribution (airflow_movielens) using a src layout and setuptools, declaring dependencies and metadata so the package can be installed with pip and used across environments. It highlights operational considerations—ensuring the scheduler, webserver, and workers all have the package installed—and offers options for sharing: install from a repository, publish to a package index, or contribute to Airflow providers. The result is a maintainable approach that reduces DAG boilerplate, promotes reuse across projects, and enables proper testing and CI/CD for custom Airflow extensions.

Building a simple pilot MVP for movie recommender project
Hello from the movie rating API
Authenticating to the ratings endpoint
Ratings returned by the ratings endpoint of the API
Adding our movie API connection in the Airflow web UI
In the standard (non-deferrable) method of running tasks, each task needs to be assigned a worker. However, the deferrable approach transfers all the tasks to the Triggerer.
Deferrable sensor execution sequence. Task execution begins in the scheduler. Once the task is ready to run, the execution of the sensor moves to a worker (1). The worker then passes the execution of the deferred task to the trigger (2) using the defer method. The trigger spends most of the runtime waiting for the external task to be completed. Once it finds new records, the trigger returns the execution to the worker (3). The worker then runs any remaining commands defined in the execute_complete method. Finally, the scheduler (4) marks the task as successful and proceeds with the execution of any downstream tasks.
Deferred task in the Airflow UI

Summary

  • You can extend Airflow’s built-in functionality by building custom components that fit your specific use cases. In our experience, two use cases in which custom operators are particularly powerful are as follows:
    • Running tasks on systems that are not natively supported by Airflow (e.g., new cloud services, databases, etc.)
    • Providing operators/sensors/hooks for commonly performed operations, such that these are easy to implement by people in your team across DAGs
    • Of course, this is by no means an exhaustive list, and there may be many other situations in which you would want to build your own components.
  • In Airflow, you can create your own components by defining Python classes and incorporating custom logic. These components can be imported and used in your DAGs to achieve any custom functionality required by your Airflow pipeline.
  • Custom hooks allow you to interact with systems that do not have support built into Airflow.
  • Custom operators can be created to perform tasks that are specific to your workflows and are not covered by built-in operators.
  • Custom sensors allow you to build components for waiting on (external) events.
  • Deferrable operators in Airflow have the ability to pause or "defer" tasks, freeing up Airflow workers during waiting periods. This is particularly useful when a task needs to wait for an external system to complete a process.
  • The triggerer is a component in Airflow that’s responsible for managing deferred tasks. While a worker in Airflow handles one task at a time, a triggerer can manage multiple deferred tasks asynchronously, making the process of managing workers more efficient.
  • While many operators have a deferrable version, you can also create your own custom deferrable operators if your workflow requires a unique logic implementation.
  • To incorporate deferrable functionality, one could either integrate both deferrable and non-deferrable logic in a single class or create separate classes where each class implements either deferrable or non-deferrable logic. Both approaches are valid, but the choice depends on the complexity and needs of the implementation.
  • Code containing custom operators, hooks, sensors, and so on can be structured by implementing them in a (distributable) Python library.
  • Custom hooks/operators/sensors must be installed with their dependencies on your Airflow cluster before they can be used. This can be tricky if you do not have permission to install software on the cluster or if you have software with conflicting dependencies.
  • Some people prefer to rely on generic operators such as the built-in Docker-Operator and the KubernetesPodOperator to execute their tasks. An advantage of this approach is that you can keep your Airflow installation lean, as Airflow is only coordinating containerized jobs; you can keep all dependencies of specific tasks with the container. We’ll focus on this approach further in a future chapter.

FAQ

Why would I build a custom hook in Airflow, and what problems does it solve?Custom hooks encapsulate all logic required to talk to an external system (auth, base URL, pagination, retries). Benefits: - Reuse across operators and DAGs - Centralized credential management via Airflow Connections (no hardcoded secrets) - Cleaner DAGs (less boilerplate) - Easier testing and maintenance
How do I read credentials and connection details in a hook without hardcoding them?Use BaseHook.get_connection(conn_id). From the returned connection object, read schema, host, port, login, and password to configure a requests.Session (or other client). Provide sensible class-level defaults if fields are missing. Example flow: config = self.get_connection(self._conn_id) -> build base_url -> set session.auth when login/password exist.
What should a hook’s get_conn return, and how can I avoid repeated metastore lookups?Return a lightweight object with the client/session and the resolved base URL (or just the client). Cache them on the hook instance (e.g., self._session, self._base_url). On first call, fetch config from the metastore and build the session; on subsequent calls, return the cached values to avoid extra DB trips.
When should I create a custom operator instead of using PythonOperator?Prefer a custom operator when you repeat the same task pattern across DAGs (e.g., fetch ratings and write to a file). Advantages: - Less DAG boilerplate - Stronger encapsulation and consistent error handling/logging - Built-in templating for dates/paths - Clear, reusable interface (conn_id, start_date, end_date, output_path)
How do template fields work in a custom operator?Define template_fields on the operator class (e.g., ("_start_date", "_end_date", "_output_path")). Airflow renders Jinja expressions in these attributes before execute runs, so you can use {{ data_interval_start | ds }} and {{ data_interval_end | ds }} for time-aware behavior and partitioned outputs.
What’s the difference between an operator and a sensor, and how do I implement a custom sensor?- Operator (BaseOperator): does work in execute - Sensor (BaseSensorOperator): waits for a condition in poke(context) -> bool; returns True to proceed, False to wait and retry. To build one, implement poke to cheaply test availability (e.g., try next(...) on a generator from your hook with batch_size=1) and rely on built-in rescheduling/timeouts.
What are deferrable operators/sensors, and why should I use them?Deferrable tasks free worker slots while waiting on external systems by moving the wait to the Triggerer (async). Use when waits are long or numerous (e.g., file arrival, long external jobs). Key pieces: - In execute, call defer(trigger=..., method_name="execute_complete", timeout=...) - Provide a Trigger (serialize + async run) - Keep run lightweight and asynchronous (e.g., await asyncio.sleep(...))
How do I package hooks/operators/sensors for reuse across projects?Use a simple setuptools package: - src layout: src/airflow_movielens/{__init__.py, hooks.py, operators.py, sensors.py} - setup.py with name, version, install_requires (e.g., apache-airflow, requests), packages=find_packages("src"), package_dir={"": "src"} - Install with pip (pip install ./airflow-movielens) and ensure it’s available to scheduler, webserver, and workers.
How do I configure and use an HTTP connection for my hook/operator?In the Airflow UI: Admin → Connections → Create. Set Conn Id (e.g., movielens), Conn Type = HTTP, host/schema/port, login/password. In code, pass conn_id="movielens" to your hook/operator. This centralizes credentials, enables rotation via UI, and avoids storing secrets in DAG code.
What are common pitfalls and best practices when extending Airflow?- Encapsulate service logic in hooks; avoid exposing internal clients widely - Cache connections in hooks; close sessions/clients when done - Use minimal work in sensors (fetch just enough to know availability) - For deferrable tasks, keep Trigger.run async and lightweight - Add template_fields for time-partitioned paths/parameters - Provide clear operator params and sensible defaults - Log meaningfully; handle errors; add retries as appropriate - Package and version your components; test them independently

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free