Task yang membaca atau menulis data pada layanan eksternal memerlukan koneksi ke layanan tersebut, dan kredensial yang tertulis langsung di dalam kode DAG ikut tersalin ke repository setiap kali DAG dikirim. Topik ini membahas hook, yaitu kelas pembungkus antarmuka ke layanan eksternal yang disediakan paket provider, beserta manajemen koneksi yang menjadi dasar kerjanya. Pembahasan mencakup cara hook mengambil kredensial dari Connection serta cara mendaftarkan koneksi melalui antarmuka web, CLI, dan variabel lingkungan. Contoh lengkapnya dijalankan menggunakan SqliteHook, dan pola penggunaan PostgresHook serta S3Hook juga diuraikan.
Hook sebagai abstraksi koneksi#
Hook adalah kelas pada paket provider yang menyediakan antarmuka menuju satu jenis layanan eksternal, misalnya basis data, penyimpanan objek, atau API HTTP. Paket provider yang dipasang bersama Airflow mendaftarkan hook masing-masing, sehingga setiap jenis koneksi yang didukung memiliki kelas pembungkusnya sendiri. Kelas induk semua hook bernama BaseHook, dan setiap hook menyimpan nama argumen conn_id miliknya, misalnya sqlite_conn_id pada SqliteHook dan postgres_conn_id pada PostgresHook. Metode utama hook adalah get_conn(). Metode ini membaca koneksi terdaftar melalui conn_id, menyusun parameter sambungan dari field pada Connection, lalu mengembalikan objek koneksi milik driver, yaitu pustaka yang berkomunikasi langsung dengan basis data.
Satu panggilan get_conn() membuka satu koneksi baru, sehingga task yang memanggilnya bertanggung jawab menutup objek tersebut setelah selesai. Sebagian hook juga menyediakan metode praktis seperti get_records() yang membuka koneksi, menjalankan query, dan menutupnya dalam satu langkah. Dokumentasi resmi menganjurkan hook dibuat di dalam fungsi task karena berkas DAG dipindai berkali-kali, sedangkan isi task hanya berjalan saat eksekusi.
Dibandingkan dengan koneksi mentah yang dibuka langsung di dalam task, hook memberikan dua keunggulan. Kredensial tersimpan pada Connection, sehingga kode DAG hanya menyebut conn_id tanpa pernah berisi password. Selain itu, hook yang sama dapat digunakan banyak task dan banyak DAG, dan operator pun mengandalkan hook secara internal, misalnya SQLExecuteQueryOperator yang menjalankan SQL melalui hook untuk koneksi terdaftar.
Manajemen koneksi#
Connection menyimpan kumpulan field kredensial untuk satu layanan eksternal. Field utamanya adalah conn_id sebagai pengenal unik dan conn_type sebagai jenis layanannya, disertai field host, schema, login, password, port, serta extra berupa JSON untuk parameter tambahan. Airflow menyediakan tiga cara mengelolanya, yaitu melalui antarmuka web, melalui perintah CLI seperti airflow connections add dan airflow connections get, serta melalui variabel lingkungan bernama AIRFLOW_CONN_<CONN_ID> yang berisi URI koneksi. Nilai conn_type menentukan kelas hook yang melayani koneksi tersebut, sehingga koneksi bertipe sqlite dilayani SqliteHook dan koneksi bertipe postgres dilayani PostgresHook.
Latihan pada topik ini menggunakan koneksi sqlite yang mengarah ke berkas basis data di /tmp/ra-hook-basis-data/nilai.db. Pada hook sqlite, field host berisi path berkas basis data alih-alih nama server. Lingkungan latihannya disiapkan pada folder terpisah, skema basis data metadata dimigrasikan sekali, lalu koneksinya didaftarkan melalui CLI. Koneksi lama dihapus terlebih dahulu agar urutan perintah dapat dijalankan ulang. Keluaran perintah pendaftaran bercampur dengan log berstempel waktu, sehingga yang ditampilkan hanya tabel konfirmasinya mulai dari baris header.
# lingkungan latihan terpisah agar DAG bawaan tidak ikut dipindai
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
export AIRFLOW__CORE__LOAD_EXAMPLES=False# siapkan skema basis data metadata
# log migrasi berisi stempel waktu, sehingga tidak ditampilkan
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
airflow db migrate > /dev/null 2>&1# daftarkan koneksi menuju berkas basis data latihan
# koneksi lama dihapus agar perintah dapat dijalankan ulang
# log berstempel waktu dikecualikan dengan menampilkan tabel konfirmasinya saja
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
airflow connections delete sqlite_default > /dev/null 2>&1 || true
airflow connections add sqlite_default --conn-type sqlite --conn-host /tmp/ra-hook-basis-data/nilai.db | grep -A 2 "^conn_id"conn_id | conn_type | host | login | port | extra
===============+===========+==================================+=======+======+======
sqlite_default | sqlite | /tmp/ra-hook-basis-data/nilai.db | None | None | {}Konfirmasinya berupa tabel yang memuat field koneksi yang tersimpan, dengan conn_type bernilai sqlite dan field host berisi path berkas basis data latihan. Setelah koneksi terdaftar, kode DAG cukup menyebut pengenal sqlite_default, dan seluruh rincian sambungannya tersimpan pada Connection.
Membaca tabel nilai melalui SqliteHook#
Basis data latihan yang dibaca melalui SqliteHook dibuat oleh berkas isi_db.py, yaitu skrip yang membuat berkas sqlite berisi tabel nilai dengan lima baris nilai ujian. Penghapusan tabel di awal skrip membuatnya dapat dijalankan ulang tanpa menduplikasi baris, dan skrip ini dijalankan sekali sebelum DAG dieksekusi. Skrip berakhir tanpa keluaran apa pun, dan isinya terbukti benar ketika DAG membaca basis data tersebut pada langkah berikutnya.
import sqlite3
from pathlib import Path
PATH_DB = Path("/tmp/ra-hook-basis-data/nilai.db")
NILAI = [("Ali", 82), ("Budi", 74), ("Citra", 91), ("Dewi", 66), ("Eka", 78)]
PATH_DB.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(PATH_DB)
try:
conn.execute("DROP TABLE IF EXISTS nilai")
conn.execute("CREATE TABLE nilai (siswa TEXT, nilai INTEGER)")
conn.executemany("INSERT INTO nilai VALUES (?, ?)", NILAI)
conn.commit()
finally:
conn.close()# buat basis data latihan beserta tabel dan barisnya
# skrip berakhir tanpa keluaran apa pun
python3 isi_db.pyBerkas dag_sqlite.py memuat satu DAG bernama dag_sqlite dengan satu task yang membaca dan merangkum tabel nilai melalui SqliteHook. Hook dibuat di dalam fungsi task dengan sqlite_conn_id="sqlite_default", yaitu pengenal koneksi yang didaftarkan pada bagian sebelumnya. Pola pertama menggunakan get_records(), yang menjalankan query dan mengembalikan seluruh barisnya tanpa penanganan koneksi manual. Pola kedua menggunakan get_conn(), yang mengembalikan objek koneksi DBAPI, yaitu objek yang mengikuti standar antarmuka Python menuju basis data dan dikelola sendiri oleh task. Pada pola kedua, task membuat cursor, yaitu objek untuk menelusuri baris hasil query, menjalankan query, mengambil hasilnya melalui fetchone(), lalu menutup koneksi pada blok finally agar tetap tertutup ketika query error.
from airflow.providers.sqlite.hooks.sqlite import SqliteHook
from airflow.sdk import dag, task
@dag(
dag_id="dag_sqlite",
schedule=None,
description="Membaca dan merangkum tabel nilai melalui SqliteHook.",
)
def dag_sqlite():
@task
def rangkum_nilai():
hook = SqliteHook(sqlite_conn_id="sqlite_default")
# get_records membuka koneksi, menjalankan query, lalu menutupnya
baris = hook.get_records("SELECT siswa, nilai FROM nilai ORDER BY siswa")
for siswa, nilai in baris:
print(f"{siswa}: {nilai}")
# get_conn memberi koneksi DBAPI yang ditutup sendiri oleh task
conn = hook.get_conn()
try:
cursor = conn.cursor()
cursor.execute("SELECT ROUND(AVG(nilai), 2) FROM nilai")
rata_rata = cursor.fetchone()[0]
cursor.close()
finally:
conn.close()
print(f"rata-rata nilai: {rata_rata}")
return rata_rata
rangkum_nilai()
dag_sqlite()Langkah berikutnya dijalankan berurutan. Berkas DAG diparse terlebih dahulu sebagai pemeriksaan, disalin ke folder dags, lalu dijalankan melalui airflow dags test. Log perintahnya panjang dan bertanda waktu, sehingga diarahkan ke berkas.
# uji bahwa berkas DAG dapat diparse tanpa keluaran apa pun
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
python3 dag_sqlite.py# salin berkas DAG ke folder yang dipindai Airflow
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
mkdir -p "$AIRFLOW_HOME/dags"
cp dag_sqlite.py "$AIRFLOW_HOME/dags/"# jalankan seluruh task DAG sekali secara lokal
# log panjang dan bertanda waktu, sehingga diarahkan ke berkas
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
airflow dags test dag_sqlite 2026-11-03 > "$AIRFLOW_HOME/dags-test.log" 2>&1# tampilkan pernyataan SQL hook dan baris keluaran task dari berkas log
export AIRFLOW_HOME=/tmp/ra-hook-basis-data
grep -oE "Running statement: SELECT siswa, nilai FROM nilai ORDER BY siswa, parameters: None" "$AIRFLOW_HOME/dags-test.log"
grep -oE "(Ali|Budi|Citra|Dewi|Eka): [0-9]+|rata-rata nilai: [0-9.]+" "$AIRFLOW_HOME/dags-test.log"Running statement: SELECT siswa, nilai FROM nilai ORDER BY siswa, parameters: None
Ali: 82
Budi: 74
Citra: 91
Dewi: 66
Eka: 78
rata-rata nilai: 78.2Keluaran task memperlihatkan mekanisme hook secara nyata. Baris Running statement berasal dari logger milik SqliteHook yang mencatat pernyataan SQL sebelum dijalankan, sehingga kelas hook yang melayani koneksi terbaca langsung dari log. Lima baris nilai ditampilkan terurut menurut nama siswa, dan rata-rata 78.2 diperoleh melalui koneksi kedua pada task yang sama. Log juga menunjukkan task berakhir success dan dag run ditandai successful, yang berarti koneksi terbaca dari Connection terdaftar tanpa satu pun kredensial pada kode DAG.
Hook untuk Postgres dan S3#
PostgresHook melayani koneksi bertipe postgres dan termasuk keluarga yang sama dengan SqliteHook, yaitu turunan kelas DbApiHook dari provider common.sql. Karena itu, metodenya sama, yaitu get_conn(), get_records(), get_first(), run(), dan insert_rows(). Argumen koneksi bernama postgres_conn_id secara bawaan menunjuk koneksi bernama postgres_default. Hook ini tersedia pada paket apache-airflow-providers-postgres, dan pola penggunaannya identik dengan contoh sqlite pada bagian sebelumnya.
# tidak-dijalankan
from airflow.providers.postgres.hooks.postgres import PostgresHook
hook = PostgresHook(postgres_conn_id="postgres_default")
baris = hook.get_records("SELECT kelas, COUNT(*) FROM nilai_ujian GROUP BY kelas")
hook.insert_rows(table="rekap_kelas", rows=[("A", 3), ("B", 2)])S3Hook dari paket apache-airflow-providers-amazon melayani penyimpanan objek S3 dan tidak berbasis DBAPI, karena antarmukanya berupa operasi objek dan bukan query. Argumennya bernama aws_conn_id dan secara bawaan menunjuk koneksi aws_default, sedangkan kredensial AWS tersimpan pada field koneksi tersebut. Metode yang sering digunakan antara lain check_for_key() untuk memeriksa keberadaan objek, read_key() untuk membaca isi objek, load_string() untuk menulis string sebagai objek, serta get_key() yang mengembalikan kunci beserta metadatanya.
# tidak-dijalankan
from airflow.providers.amazon.aws.hooks.s3 import S3Hook
hook = S3Hook(aws_conn_id="aws_default")
hook.load_string("arsip nilai ujian", key="nilai/2026-11-03.csv", bucket_name="arsip-nilai", replace=True)
isi = hook.read_key(key="nilai/2026-11-03.csv", bucket_name="arsip-nilai")Kedua contoh di atas tidak dijalankan pada lingkungan latihan ini karena memerlukan layanan PostgreSQL dan S3 yang belum tersedia. Meski begitu, polanya sama dengan SqliteHook, yaitu membuat hook dengan conn_id, memanggil metodenya di dalam task, dan membiarkan kredensial tersimpan pada Connection.
Kesalahan umum#
- Mengimpor hook dari provider yang belum terpasang. Berkas DAG gagal diparse dengan pesan
ModuleNotFoundError, sehingga DAG tidak muncul sama sekali. Perbaikannya dengan memasang paket provider yang sesuai, misalnyapip install apache-airflow-providers-postgres, pada lingkungan tempat scheduler dan worker berjalan. - Mengisi
conn_idyang belum terdaftar. Pencarian koneksi terjadi saat task berjalan, bukan saat berkas DAG diparse, sehingga errornya baru muncul di tengah eksekusi. Perbaikannya dengan mendaftarkan koneksi padaAIRFLOW_HOMEyang sama dengan lingkungan eksekusi dan menyamakan penulisanconn_id-nya. - Membuat hook pada tingkat modul berkas DAG. Hook mencari koneksinya setiap kali berkas dipindai, sehingga parser mengakses basis data metadata berulang kali tanpa keperluan. Perbaikannya dengan membuat hook di dalam fungsi task, sebagaimana contoh pada topik ini.
Ringkasan#
- Hook menyediakan antarmuka ke layanan eksternal, membaca kredensial dari Connection melalui
conn_id, dan mengembalikan objek koneksi yang siap digunakan melaluiget_conn(). - Connection dikelola melalui antarmuka web, CLI, atau variabel lingkungan
AIRFLOW_CONN_<CONN_ID>, danconn_typepada koneksi menentukan kelas hook yang melayaninya. - SqliteHook membaca field
hostsebagai path berkas basis data, danget_records()sertaget_conn()memberi dua tingkat kendali koneksi yang sama-sama ditutup setelah digunakan. - PostgresHook mengikuti antarmuka DbApiHook yang sama dengan SqliteHook, sedangkan S3Hook mengekspos operasi objek seperti
read_key()danload_string()dengan kredensial pada koneksiaws_default.
Referensi#
- Creating a custom Operator — Apache Airflow — Bagian Hooks yang menjelaskan hook sebagai antarmuka ke sumber daya luar serta anjuran membuatnya di dalam method eksekusi task.
- Managing Connections — Apache Airflow — Struktur Connection dan cara mengelolanya melalui UI, CLI, dan environment variable.
- SQLite Provider — Apache Airflow — Paket provider sqlite yang memuat SqliteHook beserta pemetaan koneksi defaultnya.
- PostgresHook — Apache Airflow — API PostgresHook sebagai turunan DbApiHook beserta metode query dan penyisipan barisnya.
- S3Hook — Apache Airflow — API S3Hook untuk memeriksa, membaca, dan menulis objek pada S3.