Menghubungkan aplikasi ke Elasticsearch: client resmi Java, Python, Node.js, .NET, dan Go; pola connection pooling, retry, bulk, error handling, dan async; serta best practice naming index, schema design, pencegahan mapping explosion, dan kompatibilitas versi.

Sampai episode 24 kalian berinteraksi dengan Elasticsearch lewat curl dan Kibana. Tapi dalam produk sungguhan, yang berbicara dengan Elasticsearch adalah aplikasi — backend service yang mengirim data dan menerima hasil pencarian. Cara aplikasi terhubung menentukan stabilitas dan performa di production. Episode 25 membahas client resmi Elasticsearch (Java, Python, Node.js, .NET, Go), pola integrasi penting (connection pooling, retry, bulk, error handling, async), dan best practice — naming convention index, schema design, pencegahan mapping explosion, serta kompatibilitas versi.
Elastic menyediakan client resmi untuk bahasa utama, dengan API yang konsisten di semua bahasa:
| Bahasa | Package | Contoh penggunaan |
|---|---|---|
| Java | co.elastic.clients:elasticsearch-java | Aplikasi Spring Boot |
| Python | elasticsearch | Data pipeline dan script |
| Node.js | @elastic/elasticsearch | Backend JavaScript/TypeScript |
| .NET | Elastic.Clients.Elasticsearch | Layanan .NET |
| Go | github.com/elastic/go-elasticsearch | Microservice Go |
Semua client berbicara HTTP ke REST API yang sama — artinya apa yang kalian pelajari di series ini (index, query DSL, aggregation) langsung berlaku di bahasa apa pun.
from elasticsearch import Elasticsearch
es = Elasticsearch("https://node1:9200", api_key="dUJ4dGVzdGtleQ==")
es.index(
index="produk",
id="1",
document={"name": "Kaos Polos Premium", "price": 99000, "category": "fashion"},
)
resp = es.search(index="produk", query={"match": {"name": "kaos"}}, size=5)
for hit in resp["hits"]["hits"]:
print(hit["_source"])Perhatikan api_key — sesuai episode 15, aplikasi memakai API keys, bukan user elastic.
Membuat koneksi HTTP per request adalah pemborosan. Setiap client resmi punya connection pooling bawaan: kumpulan koneksi TCP yang dipakai ulang. Konfigurasi yang perlu diperhatikan:
es = Elasticsearch(
["https://node1:9200", "https://node2:9200"],
max_connections=50,
timeout=30,
retry_on_timeout=True,
max_retries=3,
)Daftar host di atas memberi client beberapa node untuk failover otomatis. max_connections perlu disesuaikan dengan concurrency aplikasi — terlalu kecil membatasi throughput, terlalu besar membebani node.
Jaringan tidak sempurna: request bisa gagal karena timeout, koneksi terputus, atau node sibuk. Client resmi punya retry bawaan, tapi strategi retry yang baik bersifat selektif:
| Situasi | Strategi |
|---|---|
| Timeout / koneksi terputus | Retry (client otomatis ke node lain) |
429 Too Many Requests | Retry dengan backoff (jangan paksa) |
409 Version Conflict | Jangan retry — log dan tangani |
400 Bad Request (query salah) | Jangan retry — bug di kode kalian |
Prinsipnya: retry hanya untuk kegagalan transien (jaringan, overload), bukan kegagalan permanen (request invalid). Retry yang membabi buta justru memperburuk node yang sudah sibuk.
Di aplikasi, jangan pernah index dokumen satu per satu — gunakan bulk dengan pola helper client:
from elasticsearch.helpers import bulk
actions = [
{"_index": "produk", "_id": str(i), "_source": {"name": f"produk {i}", "price": i * 1000}}
for i in range(10000)
]
success, failed = bulk(es, actions, chunk_size=1000)
print(f"{success} berhasil, {failed} gagal")Helper bulk membagi aksi menjadi chunk dan menangani retry per aksi. Ini pola standar untuk aplikasi yang menulis data massal — gabungkan dengan pengaturan replica 0 dan refresh interval yang dibahas di episode 19 saat melakukan load besar.
Setiap client punya kelas exception yang khas:
from elasticsearch import Elasticsearch
from elasticsearch.exceptions import NotFoundError, ConnectionTimeout
try:
resp = es.get(index="produk", id="999")
except NotFoundError:
print("dokumen tidak ada")
except ConnectionTimeout:
print("koneksi timeout, coba lagi")Menangkap exception spesifik, bukan Exception umum, membuat aplikasi bisa merespons dengan tepat — misalnya menampilkan 404 untuk dokumen hilang, atau menampilkan pesan retry untuk timeout.
Untuk aplikasi dengan beban tinggi, client mendukung mode asinkron. Di Python: AsyncElasticsearch; di Node.js: API berbasis Promise sudah async secara default:
import { Client } from "@elastic/elasticsearch";
const client = new Client({ node: "https://node1:9200", auth: { apiKey: "dUJ4dGVzdGtleQ==" } });
const results = await client.search({
index: "produk",
query: { match: { name: "kaos" } },
});
console.log(results.hits.hits.map(h => h._source));Async memungkinkan banyak request berjalan paralel tanpa memblokir thread — penting di aplikasi event-driven.
Pola penamaan index yang baik: domain-data-tahun.mm.dd — misalnya orders-2026.08.03. Nama yang konsisten memudahkan template, data streams, dan ILM (episode 10-11). Hindari karakter khusus dan underscore di awal.
Desain mapping (episode 5) bersama tim aplikasi, bukan setelah data masuk. Tanyakan untuk tiap field: apa tipe sebenarnya, apakah perlu di-search, apakah perlu di-aggregate, dan berapa lama data ini bertahan. Keputusan schema yang salah murah di awal, mahal saat reindex.
Di aplikasi, jangan pernah menulis nama field dinamis dari input pengguna (misalnya user.<id>.score) tanpa batas. Gunakan dynamic: strict di production dan batas total_fields.limit (episode 5). Ini pelajaran yang sama pentingnya di aplikasi seperti di pipeline.
Client minor version boleh beda dengan server, tapi hindari gap major. Elasticsearch 8.x kompatibel dengan client 7.17+ (client menjanjikan backward compatibility), tapi yang paling aman: ikuti major version yang sama dan upgrade client sebelum server saat melakukan upgrade major (episode 30). Cek versi cluster dengan curl -s localhost:9200.
Tip
Jadikan API key sebagai satu-satunya kredensial aplikasi: buat per-aplikasi dengan role terbatas, simpan di secret manager (bukan di kode!), dan rotasi berkala. Kombinasikan dengan timeout dan retry yang terukur — aplikasi yang resilient tidak menyerah di request pertama, tapi juga tidak meronta-ronta memukuli cluster yang sedang sibuk.
Index satu-per-satu di loop. Gunakan bulk helper dengan chunk yang terukur.
Retry untuk error 400/409. Hanya retry kegagalan transien.
Tanpa connection pooling dan timeout. Default client sudah baik, tapi atur sesuai beban.
API key hardcode di source code. Simpan di secret manager dan environment.
Field dinamis tanpa batas. Mapping explosion menghancurkan cluster — batasi sejak desain.
Di episode 25 kalian menguasai integrasi aplikasi: client resmi untuk Java, Python, Node.js, .NET, dan Go; pola connection pooling, retry selektif, bulk helper, error handling dengan exception spesifik, dan operasi async; serta best practice naming index, schema design bersama tim, pencegahan mapping explosion, dan kompatibilitas versi.
Inti yang harus dibawa pulang:
Aplikasi yang terhubung adalah bagian dari sistem yang lebih besar. Di episode 26 kita bahas Elasticsearch dalam microservices architecture: centralized logging lintas service, structured logging dan trace IDs, distributed tracing dengan Elastic APM dan OpenTelemetry, service maps, serta pola service discovery dengan Elasticsearch. Sampai jumpa!