Bagi seorang developer, command-line interface (CLI) adalah salah satu alat paling produktif: cepat, ringan, dan mudah diotomatisasi maupun dijadwalkan lewat cron. Namun membangun CLI yang baik secara manual—dengan pesan bantuan --help otomatis, validasi input, dan pesan error yang ramah—bisa sangat merepotkan. Di sinilah Typer berperan: framework CLI modern buatan Sebastián Ramírez, penulis FastAPI, yang memanfaatkan type hints Python untuk menghasilkan CLI lengkap hanya dari fungsi biasa.
Apa Itu Typer dan Mengapa Memilihnya?
Typer dibangun di atas Click, library CLI yang sudah matang dan banyak dipakai. Keunggulan utamanya: kamu cukup menulis fungsi Python biasa dengan type hints, dan Typer otomatis menghasilkan parser argumen, halaman bantuan, serta validasi tipe. Tidak perlu lagi deklarasi verbose seperti pada argparse. Hasilnya, kode CLI jadi lebih pendek, lebih mudah dibaca, dan lebih mudah diuji.
Persiapan Lingkungan
Sebelum mulai, pastikan Python 3.8+ sudah terpasang. Saya menyarankan memakai virtual environment agar dependensi proyek tetap rapi. Jika kamu ingin alur yang lebih cepat, coba uv, package manager modern pengganti pip dan venv. Untuk tutorial ini kita cukup menginstal Typer beserta ekstranya:
# Buat dan aktifkan virtual environment
python -m venv .venv
source .venv/bin/activate
# Install Typer + Rich (ekstra "all" sudah termasuk Rich dan shell completion)
pip install "typer[all]"
Ekstra [all] menginstal rich untuk output berwarna, shellingham untuk deteksi shell, dan click untuk autocompletion. Untuk proyek produksi, kamu bisa menginstal typer dan rich secara terpisah agar lebih ringan.
CLI Pertama dengan @app.command()
Inti dari Typer adalah objek typer.Typer() dan decorator @app.command(). Jika kamu belum paham cara kerja decorator di Python, baca dulu panduan decorator kami. Mari buat CLI sederhana bernama halo.py:
# halo.py
import typer
app = typer.Typer()
@app.command()
def halo(nama: str):
"""Menyapa pengguna dengan nama yang diberikan."""
typer.echo(f"Halo, {nama}!")
if __name__ == "__main__":
app()
Jalankan dengan python halo.py Budi dan kamu akan melihat output Halo, Budi!. Yang menarik, coba jalankan python halo.py --help—Typer otomatis membuat halaman bantuan lengkap dengan deskripsi yang diambil dari docstring fungsi. Ketik argumen yang salah, dan Typer langsung memberi tahu error dengan saran yang jelas.
Menambahkan Argumen dan Opsi
Typer membedakan dua jenis input: argumen (posisional, wajib) dan opsi (ditulis dengan flag seperti --verbose). Dengan type hints, keduanya otomatis dikenali. Berikut contoh kalkulator sederhana:
# kalkulator.py
import typer
app = typer.Typer()
@app.command()
def tambah(
a: int = typer.Argument(..., help="Bilangan pertama"),
b: int = typer.Argument(..., help="Bilangan kedua"),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Tampilkan detail"),
):
"""Menjumlahkan dua bilangan bulat."""
hasil = a + b
if verbose:
typer.echo(f"{a} + {b} = {hasil}")
else:
typer.echo(hasil)
if __name__ == "__main__":
app()
Perhatikan bahwa Typer membaca tipe int dan otomatis mengonversi string dari terminal menjadi integer. Kalau pengguna mengetik python kalkulator.py tambah tiga 4, Typer menolak dengan pesan Invalid value for 'A': 'tiga' is not a valid integer—tanpa satu baris pun kode validasi dari kita. Opsi --verbose juga bisa dipanggil singkat sebagai -v.
Mengorganisir Perintah dengan Subcommand
Aplikasi CLI yang baik biasanya punya beberapa perintah (misal git commit, git push). Typer mendukung subcommand secara alami: cukup tambahkan beberapa fungsi ber-decorator @app.command() di dalam satu aplikasi.
# kalkulator.py (versi subcommand)
import typer
app = typer.Typer()
@app.command()
def tambah(a: int, b: int):
"""Menjumlahkan dua bilangan."""
typer.echo(f"Hasil: {a + b}")
@app.command()
def kurang(a: int, b: int):
"""Mengurangkan dua bilangan."""
typer.echo(f"Hasil: {a - b}")
@app.command()
def bagi(a: int, b: int):
"""Membagi dua bilangan."""
if b == 0:
typer.secho("Error: tidak bisa membagi dengan nol!", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
typer.echo(f"Hasil: {a / b}")
if __name__ == "__main__":
app()
Sekarang kamu bisa menjalankan python kalkulator.py tambah 5 3, python kalkulator.py kurang 10 4, dan seterusnya. Fungsi typer.secho mencetak teks berwarna (merah untuk error) ke stderr, sedangkan raise typer.Exit(code=1) menghentikan program dengan exit code non-nol—penting untuk integrasi dengan script dan CI/CD.
Output Menarik dengan Rich
Kalau kamu ingin CLI terlihat profesional, padukan Typer dengan Rich untuk tabel, warna, dan progress bar. Berikut contoh aplikasi pengelola tugas sederhana yang menyimpan data ke file JSON:
# todo.py
import json
from pathlib import Path
import typer
from rich.console import Console
from rich.table import Table
app = typer.Typer()
console = Console()
DATA_FILE = Path("tasks.json")
def muat_tugas():
if DATA_FILE.exists():
return json.loads(DATA_FILE.read_text())
return []
def simpan_tugas(tugas):
DATA_FILE.write_text(json.dumps(tugas, indent=2, ensure_ascii=False))
@app.command()
def tambah(judul: str, prioritas: str = typer.Option("normal", help="rendah/normal/tinggi")):
"""Menambahkan tugas baru ke daftar."""
tugas = muat_tugas()
tugas.append({"judul": judul, "prioritas": prioritas, "selesai": False})
simpan_tugas(tugas)
console.print(f"[green]✓[/green] Tugas '[bold]{judul}[/bold]' berhasil ditambahkan.")
@app.command()
def daftar():
"""Menampilkan semua tugas dalam bentuk tabel."""
tugas = muat_tugas()
if not tugas:
console.print("[yellow]Belum ada tugas.[/yellow]")
return
tabel = Table(title="Daftar Tugas")
tabel.add_column("No.", justify="right")
tabel.add_column("Judul")
tabel.add_column("Prioritas")
tabel.add_column("Status")
warna = {"rendah": "dim", "normal": "cyan", "tinggi": "red"}
for i, t in enumerate(tugas, start=1):
status = "[green]selesai[/green]" if t["selesai"] else "[yellow]belum[/yellow]"
tabel.add_row(str(i), t["judul"], f"[{warna[t['prioritas']]}]{t['prioritas']}[/]", status)
console.print(tabel)
if __name__ == "__main__":
app()
Coba jalankan python todo.py tambah "Belajar Typer" --prioritas tinggi lalu python todo.py daftar. Kamu akan melihat tabel rapi dengan warna sesuai prioritas. Rich memakai markup sederhana di dalam kurung siku, misalnya [red]teks merah[/red] dan [bold]teks tebal[/bold].
Typer dan Rich sama-sama populer di ekosistem Python dan didesain untuk saling melengkapi. Kamu juga bisa mengganti typer.echo dengan console.print dari Rich agar seluruh output CLI kamu konsisten.
Validasi Otomatis dengan Enum dan Type Hints
Salah satu fitur terbaik Typer adalah validasi berbasis tipe. Dengan Enum, nilai opsi langsung dibatasi pada pilihan yang valid, dan --help menampilkan pilihan tersebut secara otomatis:
# todo.py (validasi prioritas dengan Enum)
from enum import Enum
import typer
class Prioritas(str, Enum):
rendah = "rendah"
normal = "normal"
tinggi = "tinggi"
app = typer.Typer()
@app.command()
def tambah(judul: str, prioritas: Prioritas = Prioritas.normal):
"""Menambahkan tugas dengan prioritas tervalidasi."""
typer.echo(f"Menambahkan '{judul}' dengan prioritas {prioritas.value}")
if __name__ == "__main__":
app()
Sekarang python todo.py tambah "Tugas" --prioritas super-tinggi akan ditolak otomatis dengan pesan yang mencantumkan pilihan valid (rendah, normal, tinggi). Pola yang sama berlaku untuk tipe lain seperti datetime, Path, hingga Decimal. Konsep validasi data lewat type hints ini juga serupa dengan yang dilakukan Pydantic di ranah model data.
Testing CLI dengan CliRunner
CLI juga perlu diuji. Typer menyediakan CliRunner yang memungkinkan kamu memanggil perintah dan memeriksa output-nya tanpa benar-benar berinteraksi dengan terminal. Jika belum familiar dengan pytest, pelajari dasarnya dulu. Berikut contohnya:
# test_kalkulator.py
from typer.testing import CliRunner
from kalkulator import app
runner = CliRunner()
def test_tambah():
result = runner.invoke(app, ["tambah", "3", "4"])
assert result.exit_code == 0
assert "7" in result.output
def test_bagi_dengan_nol():
result = runner.invoke(app, ["bagi", "10", "0"])
assert result.exit_code == 1
assert "tidak bisa membagi dengan nol" in result.output
Jalankan dengan pytest test_kalkulator.py -v. Kedua test akan lolos karena CliRunner menangkap output dan exit code dari aplikasi Typer kita. Inilah salah satu alasan kenapa Typer disukai: kode CLI yang kamu tulis sekaligus mudah diuji seperti fungsi biasa.
Kesimpulan
Typer membuktikan bahwa membuat CLI yang profesional tidak harus rumit. Dengan type hints, kamu mendapat argumen yang tervalidasi, halaman bantuan otomatis, dan subcommand yang rapi—semuanya tanpa boilerplate berlebihan. Langkah berikutnya yang bisa kamu eksplorasi: menambahkan autocompletion shell, mempublikasikan tool ke PyPI, atau membungkus proses yang lama dengan rich.progress. Selamat membangun CLI pertamamu!