<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="id"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://bandithijo.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bandithijo.github.io/" rel="alternate" type="text/html" hreflang="id" /><updated>2026-03-11T05:46:47+08:00</updated><id>https://bandithijo.github.io/feed.xml</id><title type="html">BanditHijo.dev</title><subtitle>Here&apos;s where I started writing some notes that someday I will call it, a book</subtitle><author><name>Rizqi Nur Assyaufi</name></author><entry><title type="html">Menggabungkan Beberapa Sub-Region Geofabrik</title><link href="https://bandithijo.github.io/blog/menggabungkan-beberapa-sub-region-geofabrik" rel="alternate" type="text/html" title="Menggabungkan Beberapa Sub-Region Geofabrik" /><published>2026-02-23T01:46:00+08:00</published><updated>2026-02-23T01:46:00+08:00</updated><id>https://bandithijo.github.io/blog/menggabungkan-beberapa-sub-region-geofabrik</id><content type="html" xml:base="https://bandithijo.github.io/blog/menggabungkan-beberapa-sub-region-geofabrik"><![CDATA[<h2 id="pendahuluan">Pendahuluan</h2>

<p>Saya tidak ingin mengunduh data geofabrik untuk region Indonesia yang berukuran besar (per-tulisan ini dibuat, 1.6 GB), karena saya hanya memerlukan data untuk tiga sub-region: pulau Sumatra (258 MB), Java (847 MB), dan Kalimantan (138 MB). Oleh karena itu, saya hanya akan mengunduh data untuk ketiga sub-region tersebut dan menggabungkannya menjadi satu file .osm.pbf.</p>

<h2 id="mengunduh-data-sub-region-geofabrik">Mengunduh Data Sub-Region Geofabrik</h2>

<p>Unduh data untuk ketiga sub-region yang diperlukan dari situs Geofabrik:</p>
<ol>
  <li><a href="https://download.geofabrik.de/asia/indonesia/sumatra-latest.osm.pbf">https://download.geofabrik.de/asia/indonesia/sumatra-latest.osm.pbf</a></li>
  <li><a href="https://download.geofabrik.de/asia/indonesia/java-latest.osm.pbf">https://download.geofabrik.de/asia/indonesia/java-latest.osm.pbf</a></li>
  <li><a href="https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf">https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf</a></li>
</ol>

<p>Letakkan ketiga file .osm.pbf yang telah diunduh ke dalam satu folder, misal: <code class="language-plaintext highlighter-rouge">geofabrik/</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd geofabrik/
$ wget -c https://download.geofabrik.de/asia/indonesia/sumatra-latest.osm.pbf
$ wget -c https://download.geofabrik.de/asia/indonesia/java-latest.osm.pbf
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>geofabrik/
│ java-latest.osm.pbf
│ kalimantan-latest.osm.pbf
└ sumatra-latest.osm.pbf
</code></pre></div></div>

<h2 id="menggabungkan-data-sub-region-dengan-osmium-tool">Menggabungkan Data Sub-Region dengan Osmium Tool</h2>

<p>Setelah ketiga file .osm.pbf untuk sub-region yang diperlukan telah diunduh, langkah selanjutnya adalah menggabungkannya menjadi satu file .osm.pbf menggunakan Osmium Tool.</p>

<p>Instalasi Osmium Tool dapat dilakukan dengan mengikuti petunjuk di situs resminya: <a href="https://osmcode.org/osmium-tool/">https://osmcode.org/osmium-tool/</a>.</p>

<p>Setelah Osmium Tool terinstal, jalankan perintah berikut untuk menggabungkan ketiga file .osm.pbf menjadi satu file .osm.pbf baru, misal: <code class="language-plaintext highlighter-rouge">indonesia-3pulau-latest.osm.pbf</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ osmium merge sumatra-latest.osm.pbf java-latest.osm.pbf kalimantan-latest.osm.pbf -o indonesia-3pulau-latest.osm.pbf
</code></pre></div></div>

<p>Tunggu hingga proses penggabungan selesai.</p>

<h2 id="kesimpulan">Kesimpulan</h2>

<p>Dalam catatan ini, saya telah mendemonstrasikan proses penggabungan data untuk pulau Sumatra, Java, dan Kalimantan menjadi satu file .osm.pbf baru yang berukuran lebih kecil dibandingkan dengan data untuk seluruh region Indonesia.</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://download.geofabrik.de/asia/indonesia.html">https://download.geofabrik.de/asia/indonesia.html</a> <br />
  Diakses tanggal: 2026-02-22</p>
  </li>
  <li>
    <p><a href="https://osmcode.org/osmium-tool/">https://osmcode.org/osmium-tool/</a> <br />
  Diakses tanggal: 2026-02-22</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="openstreetmap" /><category term="geofabrik" /><summary type="html"><![CDATA[Jika data geofabrik untuk Indonesia terlalu besar dan hanya memerlukan data sub-region tertentu, misal: pulau Sumatra, Java, dan Kalimantan. Maka tiga data sub-region tersebut dapat di-merger menjadi satu file .osm.pbf.]]></summary></entry><entry><title type="html">OpenStreetMap Overpass API dengan Docker</title><link href="https://bandithijo.github.io/blog/openstreetmap-overpass-api-dengan-docker" rel="alternate" type="text/html" title="OpenStreetMap Overpass API dengan Docker" /><published>2026-02-15T10:05:00+08:00</published><updated>2026-02-15T10:05:00+08:00</updated><id>https://bandithijo.github.io/blog/openstreetmap-overpass-api-dengan-docker</id><content type="html" xml:base="https://bandithijo.github.io/blog/openstreetmap-overpass-api-dengan-docker"><![CDATA[<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-02-09-openstreetmap-overpass-api-dengan-docker/gambar_01.png" alt="Gambar 1" /></p>

<h2 id="apa-itu-overpass-api">Apa itu Overpass API?</h2>

<p>Overpass API memungkinkan kita untuk melakukan query terhadap data OpenStreetMap (OSM) yang dapat digunakan untuk mencari POI (Point of Interest) pada radius tertentu dari lokasi yang didefinisikan. Overpass API menggunakan bahasa query yang disebut Overpass QL. Dengan Overpass API, kita dapat melakukan query untuk mencari data OSM berdasarkan kriteria tertentu, seperti jenis POI, lokasi, dan lain-lain.</p>

<h2 id="overpass-api-dengan-docker">Overpass API dengan Docker</h2>

<h3 id="1-buat-docker-compose-untuk-overpass-api">1. Buat Docker Compose untuk Overpass API</h3>

<p>Buat file <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> dengan isi seperti ini,</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">overpass</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">openzonedev/overpass-api:0.7.57.2</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">osm-overpass</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">OVERPASS_META</span><span class="pi">:</span> <span class="s2">"</span><span class="s">yes"</span>
      <span class="na">OVERPASS_MODE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">init"</span>
      <span class="na">OVERPASS_PLANET_URL</span><span class="pi">:</span> <span class="s">file:///overpass/data/kalimantan-latest.osm.bz2</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">./geofabrik/kalimantan-new.latest.bz2:/overpass/data/kalimantan-latest.osm.bz2:ro</span>
      <span class="pi">-</span> <span class="s">overpass-data:/db</span>
    <span class="na">expose</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">80"</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">8081:80"</span>

<span class="na">volumes</span><span class="pi">:</span>
  <span class="na">overpass-data</span><span class="pi">:</span>
</code></pre></div></div>

<h3 id="2-persiapkan-data-osm-untuk-overpass-api">2. Persiapkan data OSM untuk Overpass API</h3>

<p>Pada contoh di atas, saya menggunakan data OSM untuk wilayah Kalimantan yang saya unduh dari Geofabrik.</p>

<blockquote>
  <p>INFO</p>

  <p>Overpass API membutuhkan file data OSM dalam format <code class="language-plaintext highlighter-rouge">.osm.bz2</code> untuk proses import data. <br />
Saya akan merubah file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> yang kita unduh dari Geofabrik menjadi format <code class="language-plaintext highlighter-rouge">.osm.bz2</code> menggunakan tool <code class="language-plaintext highlighter-rouge">osmium-tool</code>.</p>
</blockquote>

<p>File <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ini bisa didapatkan dari situs <a href="https://download.geofabrik.de/">Geofabrik</a>.</p>

<p>Setelah mengunduh file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>, letakkan file tersebut di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code> yang berada di root project. Buat direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code> jika belum ada.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mkdir geofabrik
$ cd geofabrik
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
</code></pre></div></div>

<p>Tunggu proses download selesai, setelah itu pastikan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> sudah berada di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code>.</p>

<p>Berikut adalah struktur direktori yang diharapkan setelah menambahkan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>geofabrik/
└── kalimantan-latest.osm.pbf
docker-compose.yml
</code></pre></div></div>

<h4 id="21-konversi-file-osmpbf-ke-format-osmbz2">2.1 Konversi file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ke format <code class="language-plaintext highlighter-rouge">.osm.bz2</code></h4>

<p>Untuk mengkonversi file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ke format <code class="language-plaintext highlighter-rouge">.osm.bz2</code>, bisa menggunakan tool <code class="language-plaintext highlighter-rouge">osmium-tool</code>. Pastikan <code class="language-plaintext highlighter-rouge">osmium-tool</code> sudah terinstall di sistem.</p>

<p>Jika belum, bisa menginstalnya dengan mengikuti petunjuk di <a href="https://osmcode.org/osmium-tool/">situs resmi osmium-tool</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ brew install osmium-tool
</code></pre></div></div>

<p>Setelah <code class="language-plaintext highlighter-rouge">osmium-tool</code> terinstall, jalankan perintah berikut untuk mengkonversi file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ke format <code class="language-plaintext highlighter-rouge">.osm.bz2</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ osmium cat geofabrik/kalimantan-latest.osm.pbf -o geofabrik/kalimantan-latest.osm.bz2
</code></pre></div></div>

<p>Tunggu proses konversi selesai, setelah itu pastikan file <code class="language-plaintext highlighter-rouge">.osm.bz2</code> sudah berada di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code>.</p>

<p>Sip, sekarang file <code class="language-plaintext highlighter-rouge">.osm.bz2</code> sudah siap untuk digunakan dalam proses import data oleh Overpass API.</p>

<h3 id="3-jalankan-docker-compose">3. Jalankan Docker Compose</h3>

<p>Setelah file <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> sudah siap dan file data OSM dalam format <code class="language-plaintext highlighter-rouge">.osm.bz2</code> sudah berada di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code>, jalankan perintah berikut untuk memulai Docker Compose:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose up -d
</code></pre></div></div>

<blockquote>
  <p>PERHATIAN</p>

  <p>Untuk pertama kali menjalankan docker compose, Overpass API service akan melakukan proses import data dari file <code class="language-plaintext highlighter-rouge">.osm.bz2</code> ke dalam Overpass Data, proses ini membutuhkan waktu yang cukup lama, tergantung pada ukuran file <code class="language-plaintext highlighter-rouge">.osm.bz2</code> yang digunakan. Untuk file <code class="language-plaintext highlighter-rouge">kalimantan-latest.osm.bz2</code> yang berukuran 318 MB, proses import data bisa memakan waktu sekitar 30 menit hingga 1 jam.</p>
</blockquote>

<p>Untuk melihat log dari container, bisa menggunakan perintah berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker compose logs overpass -f
</code></pre></div></div>

<p>Tunggu proses import data selesai. Biasanya akan muncul log seperti berikut ketika proses import data sudah selesai:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Overpass container initialization complete. Exiting.
</code></pre></div></div>

<p>Setelah proses import data selesai, Overpass API service sudah siap untuk digunakan. Overpass API bisa diakses melalui <code class="language-plaintext highlighter-rouge">http://localhost:8081/</code>.</p>

<h3 id="4-akses-overpass-api">4. Akses Overpass API</h3>

<h4 id="41-apistatus">4.1 /api/status</h4>

<p>Untuk memastikan bahwa Overpass API service sudah berjalan dengan baik, coba akses endpoint <code class="language-plaintext highlighter-rouge">/api/status</code> untuk melihat status dari Overpass API service.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl http://localhost:8081/api/status
</code></pre></div></div>

<p>Outputnya akan seperti ini,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Connected as: 3232252161
Current time: 2026-02-16T10:25:16Z
Rate limit: 0
</code></pre></div></div>

<h4 id="42-apiinterpreter">4.2 /api/interpreter</h4>

<p>Setelah Overpass API service sudah siap, coba lakukan query untuk mencari data OSM berdasarkan kriteria tertentu.</p>

<p>Base URL nya seperti ini,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -X POST http://localhost:8081/api/interpreter
</code></pre></div></div>

<p>Outputnya akan seperti ini,</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span>
<span class="nt">&lt;html</span> <span class="na">xmlns=</span><span class="s">"http://www.w3.org/1999/xhtml"</span> <span class="na">xml:lang=</span><span class="s">"en"</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">http-equiv=</span><span class="s">"content-type"</span> <span class="na">content=</span><span class="s">"text/html; charset=utf-8"</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;title&gt;</span>OSM3S Response<span class="nt">&lt;/title&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>

<span class="nt">&lt;p&gt;</span>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;p&gt;&lt;strong</span> <span class="na">style=</span><span class="s">"color:#FF0000"</span><span class="nt">&gt;</span>Error<span class="nt">&lt;/strong&gt;</span>: encoding error: Your input contains only whitespace. <span class="nt">&lt;/p&gt;</span>

<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>Nah, tinggal tambahkan query untuk mencari data OSM berdasarkan kriteria tertentu.</p>

<p>Gunakan juga header <code class="language-plaintext highlighter-rouge">Content-Type: application/x-www-form-urlencoded</code> untuk mengirim data query dalam format URL encoded.</p>

<p>Misalnya, untuk mencari semua POI dengan tag <code class="language-plaintext highlighter-rouge">amenity=cafe</code> pada radius 5000 meter dari titik user berada (<code class="language-plaintext highlighter-rouge">-1.2286365, 116.8941987</code>), kita bisa menggunakan query berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -X POST http://localhost:8081/api/interpreter -H "Content-Type: application/x-www-form-urlencoded" --data "data=[out:json];node[amenity=cafe](around:5000, -1.2286365, 116.8941987);out center;"
</code></pre></div></div>

<p>Outputnya dalam bentuk JSON akan seperti ini,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.6</span><span class="p">,</span><span class="w">
  </span><span class="nl">"generator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Overpass API 0.7.57.2 48842a1b"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"osm3s"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"timestamp_osm_base"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-01-18T20:51:44Z"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"elements"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9898980886</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2420011</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8730133</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Excelso - Living Plaza"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9899039726</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2437437</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8607824</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Excelso Cafe Mall Fantasy"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9899398634</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2449160</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8603869</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CPM Coffee Shop Balikpapan Baru - Damai"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9899400587</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2452571</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8597143</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Balgas Cafe - Damai"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9899406395</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2452690</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8606450</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kopi Lawe - Damai Baru"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9900996432</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2511856</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8696109</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"branch"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ruko Jalan MT Haryono"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brand"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kopi Kenangan"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brand:wikidata"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Q97221992"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"cuisine"</span><span class="p">:</span><span class="w"> </span><span class="s2">"coffee_shop"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"diet:halal"</span><span class="p">:</span><span class="w"> </span><span class="s2">"only"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kopi Kenangan"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"takeaway"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yes"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">10036595776</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">-1.2460153</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">116.8594667</span><span class="p">,</span><span class="w">
      </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"amenity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cafe"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Dialog Cafe - Balikpapan Baru"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Dari data di atas, kita bisa melihat bahwa terdapat beberapa POI dengan tag <code class="language-plaintext highlighter-rouge">amenity=cafe</code> pada radius 5000 meter dari latitude dan longitude yang saya definisikan  yang berhasil ditemukan oleh Overpass API.</p>

<p>Silahkan sesuaikan query sesuai dengan kebutuhan untuk mencari data OSM berdasarkan kriteria tertentu. Berikut adalah contoh query dalam format Overpass QL yang digunakan di atas:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[out:json];
(
  node[amenity=cafe](around:5000, -1.2286365, 116.8941987);
);
out center;
</code></pre></div></div>

<h3 id="5-stop-docker-compose">5. Stop Docker Compose</h3>

<p>Jika sudah selesai menggunakan Overpass API service, jangan lupa untuk menghentikan Docker Compose dengan perintah berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose download
</code></pre></div></div>

<h2 id="kesimpulan">Kesimpulan</h2>

<p>Saya telah mencatat langkah-langkah untuk menjalankan instance Overpass API menggunakan Docker. Dengan mengikuti langkah-langkah di atas, kita dapat dengan mudah menjalankan Overpass API service di lingkungan lokal kita dan melakukan query terhadap data OpenStreetMap untuk mencari POI berdasarkan kriteria tertentu.</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li><a href="https://overpass-api.de/">https://overpass-api.de/</a> <br />
  Diakses tanggal: 2026-02-15</li>
  <li><a href="https://osmcode.org/osmium-tool/">https://osmcode.org/osmium-tool/</a> <br />
  Diakses tanggal: 2026-02-15</li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="openstreetmap" /><category term="overpass" /><category term="overpassapi" /><summary type="html"><![CDATA[Overpass API memungkinkan kita untuk melakukan query terhadap data OpenStreetMap yang dapat digunakan untuk mencari POI (Point of Interest) pada radius tertentu dari lokasi yang didefinisikan. Pada artikel ini, saya akan mencatat langkah-langkah untuk menjalankan instance Overpass API menggunakan Docker.]]></summary></entry><entry><title type="html">Menggunakan Nominatim Self-Hosted pada Geocoder Gem</title><link href="https://bandithijo.github.io/blog/menggunakan-nominatim-self-hosted-pada-geocoder-gem" rel="alternate" type="text/html" title="Menggunakan Nominatim Self-Hosted pada Geocoder Gem" /><published>2026-02-08T23:22:00+08:00</published><updated>2026-02-08T23:22:00+08:00</updated><id>https://bandithijo.github.io/blog/menggunakan-nominatim-self-hosted-pada-geocoder-gem</id><content type="html" xml:base="https://bandithijo.github.io/blog/menggunakan-nominatim-self-hosted-pada-geocoder-gem"><![CDATA[<h2 id="pendahuluan">Pendahuluan</h2>

<p>Pada catatan sebelumnya “<a href="https://bandithijo.github.io/blog/openstreetmap-nominatim-dengan-docker">OpenStreetMap Nominatim (Geocoding Machine) dengan Docker</a>” saya telah membahas cara menjalankan instance Nominatim menggunakan Docker. Pada artikel ini, saya akan mencatat langkah-langkah untuk mengonfigurasi Geocoder gem di aplikasi Ruby on Rails agar menggunakan instance Nominatim self-hosted sebagai geocoding service lookup.</p>

<h2 id="konfigurasi-geocoder-gem">Konfigurasi Geocoder Gem</h2>

<p>Jika sebelumnya sudah memiliki config file untuk Geocoder di <code class="language-plaintext highlighter-rouge">config/initializers/geocoder.rb</code>, buka file tersebut.</p>

<p>Dan tambahkan atau modifikasi konfigurasi seperti ini,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">!</span><span class="ss">filename: </span><span class="n">config</span><span class="o">/</span><span class="n">initializers</span><span class="o">/</span><span class="n">geocoder</span><span class="p">.</span><span class="nf">rb</span>
<span class="no">Geocoder</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span>
  <span class="c1"># Geocoding options</span>
  <span class="c1"># timeout: 3,                 # geocoding service timeout (secs)</span>
  <span class="c1"># lookup: :nominatim,         # name of geocoding service (symbol)</span>
  <span class="c1"># ip_lookup: :ipinfo_io,      # name of IP address geocoding service (symbol)</span>
  <span class="c1"># language: :en,              # ISO-639 language code</span>
  <span class="c1"># use_https: false,           # use HTTPS for lookup requests? (if supported)</span>
  <span class="c1"># http_proxy: nil,            # HTTP proxy server (user:pass@host:port)</span>
  <span class="c1"># https_proxy: nil,           # HTTPS proxy server (user:pass@host:port)</span>
  <span class="c1"># api_key: nil,               # API key for geocoding service</span>
  <span class="c1"># cache: nil,                 # cache object (must respond to #[], #[]=, and #del)</span>

  <span class="c1"># Exceptions that should not be rescued by default</span>
  <span class="c1"># (if you want to implement custom error handling);</span>
  <span class="c1"># supports SocketError and Timeout::Error</span>
  <span class="c1"># always_raise: [],</span>

  <span class="c1"># Calculation options</span>
  <span class="c1"># units: :km,                 # :km for kilometers or :mi for miles</span>
  <span class="c1"># distances: :linear          # :spherical or :linear</span>

  <span class="c1"># Cache configuration</span>
  <span class="c1"># cache_options: {</span>
  <span class="c1">#   expiration: 2.days,</span>
  <span class="c1">#   prefix: 'geocoder:'</span>
  <span class="c1"># }</span>

  <span class="ss">use_https: </span><span class="kp">false</span><span class="p">,</span>
  <span class="ss">lookup: :nominatim</span><span class="p">,</span>
  <span class="ss">nominatim: </span><span class="p">{</span>
    <span class="ss">host: </span><span class="n">localhost</span><span class="p">:</span><span class="mi">8080</span>
  <span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Saya menggunakan <code class="language-plaintext highlighter-rouge">localhost:8080</code> karena pada catatan sebelumnya saya menjalankan Nominatim di Docker dengan port mapping <code class="language-plaintext highlighter-rouge">8080:8080</code> dan karena di localhost sehingga tidak menggunakan https.</p>

<p>Bisa juga disesuaikan dengan host dan port tempat Nominatim dijalankan. Misal, saya sudah punya service yang saya jalankan di <code class="language-plaintext highlighter-rouge">https://nominatim.bandithijo.dev</code>, maka konfigurasi <code class="language-plaintext highlighter-rouge">nominatim</code> akan seperti ini,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">!</span><span class="ss">filename: </span><span class="n">config</span><span class="o">/</span><span class="n">initializers</span><span class="o">/</span><span class="n">geocoder</span><span class="p">.</span><span class="nf">rb</span>
<span class="no">Geocoder</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span>
  <span class="c1"># ...</span>
  <span class="c1"># ...</span>

  <span class="ss">use_https: </span><span class="kp">true</span><span class="p">,</span>
  <span class="ss">lookup: :nominatim</span><span class="p">,</span>
  <span class="ss">nominatim: </span><span class="p">{</span>
    <span class="ss">host: </span><span class="s1">'nominatim.bandithijo.dev'</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Dengan begini saya sudah berhasil mengonfigurasi Geocoder gem untuk menggunakan Nominatim self-hosted sebagai geocoding service lookup. Selanjutnya, saya bisa menggunakan Geocoder gem untuk melakukan geocoding dan reverse geocoding pada aplikasi Ruby on Rails saya dengan menggunakan instance Nominatim yang saya jalankan sendiri.</p>

<h2 id="pengujian">Pengujian</h2>

<p>Untuk menguji apakah konfigurasi sudah benar, bisa dilakukan di Rails console.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rails console
</code></pre></div></div>

<p>Setup logger agar menampilkan request dan response dari Geocoder gem untuk mempermudah proses debugging,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">001</span><span class="o">&gt;</span> <span class="no">Geocoder</span><span class="p">.</span><span class="nf">configure</span><span class="p">(</span><span class="ss">logger: </span><span class="no">Logger</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">STDOUT</span><span class="p">))</span>
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">=&gt;</span>
<span class="p">{</span><span class="ss">:timeout</span><span class="o">=&gt;</span><span class="mi">3</span><span class="p">,</span>
 <span class="ss">:lookup</span><span class="o">=&gt;</span><span class="ss">:nominatim</span><span class="p">,</span>
 <span class="ss">:ip_lookup</span><span class="o">=&gt;</span><span class="ss">:ipinfo_io</span><span class="p">,</span>
 <span class="ss">:language</span><span class="o">=&gt;</span><span class="ss">:en</span><span class="p">,</span>
 <span class="ss">:http_headers</span><span class="o">=&gt;</span><span class="p">{},</span>
 <span class="ss">:use_https</span><span class="o">=&gt;</span><span class="kp">true</span><span class="p">,</span>
 <span class="ss">:http_proxy</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">,</span>
 <span class="ss">:https_proxy</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">,</span>
 <span class="ss">:api_key</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">,</span>
 <span class="ss">:basic_auth</span><span class="o">=&gt;</span><span class="p">{},</span>
 <span class="ss">:logger</span><span class="o">=&gt;</span>
  <span class="c1">#&lt;Logger:0x000000012837e6b8</span>
   <span class="vi">@default_formatter</span><span class="o">=</span><span class="c1">#&lt;Logger::Formatter:0x0000000124a709e8 @datetime_format=nil&gt;,</span>
   <span class="vi">@formatter</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
   <span class="vi">@level</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
   <span class="vi">@level_override</span><span class="o">=</span><span class="p">{},</span>
   <span class="vi">@logdev</span><span class="o">=</span>
    <span class="c1">#&lt;Logger::LogDevice:0x0000000128273980</span>
     <span class="vi">@binmode</span><span class="o">=</span><span class="kp">false</span><span class="p">,</span>
     <span class="vi">@dev</span><span class="o">=</span><span class="c1">#&lt;IO:&lt;STDOUT&gt;&gt;,</span>
     <span class="vi">@filename</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
     <span class="vi">@mon_data</span><span class="o">=</span><span class="c1">#&lt;Monitor:0x0000000124a70970&gt;,</span>
     <span class="vi">@mon_data_owner_object_id</span><span class="o">=</span><span class="mi">71700</span><span class="p">,</span>
     <span class="vi">@reraise_write_errors</span><span class="o">=</span><span class="p">[],</span>
     <span class="vi">@shift_age</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
     <span class="vi">@shift_period_suffix</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
     <span class="vi">@shift_size</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
     <span class="vi">@skip_header</span><span class="o">=</span><span class="kp">false</span><span class="o">&gt;</span><span class="p">,</span>
   <span class="vi">@progname</span><span class="o">=</span><span class="kp">nil</span><span class="o">&gt;</span><span class="p">,</span>
 <span class="ss">:kernel_logger_level</span><span class="o">=&gt;</span><span class="mi">2</span><span class="p">,</span>
 <span class="ss">:always_raise</span><span class="o">=&gt;</span><span class="p">[],</span>
 <span class="ss">:units</span><span class="o">=&gt;</span><span class="ss">:km</span><span class="p">,</span>
 <span class="ss">:distances</span><span class="o">=&gt;</span><span class="ss">:linear</span><span class="p">,</span>
 <span class="ss">:cache</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">,</span>
 <span class="ss">:cache_prefix</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">,</span>
 <span class="ss">:cache_options</span><span class="o">=&gt;</span><span class="p">{</span><span class="ss">:prefix</span><span class="o">=&gt;</span><span class="s2">"geocoder:"</span><span class="p">,</span> <span class="ss">:expiration</span><span class="o">=&gt;</span><span class="kp">nil</span><span class="p">},</span>
 <span class="ss">:nominatim</span><span class="o">=&gt;</span><span class="p">{</span><span class="ss">:host</span><span class="o">=&gt;</span><span class="s2">"osm.bintangdigitalasia.com/nominatim"</span><span class="p">}}</span>
</code></pre></div></div>

<p>Dapat dilihat, bahwa konfigurasi <code class="language-plaintext highlighter-rouge">nominatim</code> untuk <code class="language-plaintext highlighter-rouge">host</code> sudah sesuai dengan yang diinginkan.</p>

<p>Lalu lakukan geocoding pada alamat tertentu,</p>

<h3 id="forward-search">Forward Search</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">002</span><span class="o">&gt;</span> <span class="no">Geocoder</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="s2">"Jakarta"</span><span class="p">)</span>
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">D</span><span class="p">,</span> <span class="p">[</span><span class="mi">2026</span><span class="o">-</span><span class="mo">02</span><span class="o">-</span><span class="mi">09</span><span class="no">T00</span><span class="p">:</span><span class="mo">02</span><span class="p">:</span><span class="mf">12.686252</span> <span class="c1">#30316] DEBUG -- : Geocoder: HTTP request being made for http://localhost:8080/search?accept-language=en&amp;addressdetails=1&amp;format=json&amp;q=Jakarta</span>
<span class="o">=&gt;</span>
<span class="p">[</span><span class="c1">#&lt;Geocoder::Result::Nominatim:0x0000000127219df0</span>
  <span class="vi">@cache_hit</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
  <span class="vi">@data</span><span class="o">=</span>
   <span class="p">{</span><span class="s2">"place_id"</span><span class="o">=&gt;</span><span class="mi">3204450</span><span class="p">,</span>
    <span class="s2">"licence"</span><span class="o">=&gt;</span><span class="s2">"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright"</span><span class="p">,</span>
    <span class="s2">"osm_type"</span><span class="o">=&gt;</span><span class="s2">"relation"</span><span class="p">,</span>
    <span class="s2">"osm_id"</span><span class="o">=&gt;</span><span class="mi">6362934</span><span class="p">,</span>
    <span class="s2">"lat"</span><span class="o">=&gt;</span><span class="s2">"-6.1754049"</span><span class="p">,</span>
    <span class="s2">"lon"</span><span class="o">=&gt;</span><span class="s2">"106.8271680"</span><span class="p">,</span>
    <span class="s2">"class"</span><span class="o">=&gt;</span><span class="s2">"boundary"</span><span class="p">,</span>
    <span class="s2">"type"</span><span class="o">=&gt;</span><span class="s2">"administrative"</span><span class="p">,</span>
    <span class="s2">"place_rank"</span><span class="o">=&gt;</span><span class="mi">8</span><span class="p">,</span>
    <span class="s2">"importance"</span><span class="o">=&gt;</span><span class="mf">0.2933433333333333</span><span class="p">,</span>
    <span class="s2">"addresstype"</span><span class="o">=&gt;</span><span class="s2">"city"</span><span class="p">,</span>
    <span class="s2">"name"</span><span class="o">=&gt;</span><span class="s2">"Special Capital Region of Jakarta"</span><span class="p">,</span>
    <span class="s2">"display_name"</span><span class="o">=&gt;</span><span class="s2">"Special Capital Region of Jakarta, Java, Indonesia"</span><span class="p">,</span>
    <span class="s2">"address"</span><span class="o">=&gt;</span>
     <span class="p">{</span><span class="s2">"city"</span><span class="o">=&gt;</span><span class="s2">"Special Capital Region of Jakarta"</span><span class="p">,</span>
      <span class="s2">"ISO3166-2-lvl4"</span><span class="o">=&gt;</span><span class="s2">"ID-JK"</span><span class="p">,</span>
      <span class="s2">"region"</span><span class="o">=&gt;</span><span class="s2">"Java"</span><span class="p">,</span>
      <span class="s2">"ISO3166-2-lvl3"</span><span class="o">=&gt;</span><span class="s2">"ID-JW"</span><span class="p">,</span>
      <span class="s2">"country"</span><span class="o">=&gt;</span><span class="s2">"Indonesia"</span><span class="p">,</span>
      <span class="s2">"country_code"</span><span class="o">=&gt;</span><span class="s2">"id"</span><span class="p">},</span>
    <span class="s2">"boundingbox"</span><span class="o">=&gt;</span><span class="p">[</span><span class="s2">"-6.3744575"</span><span class="p">,</span> <span class="s2">"-4.9993635"</span><span class="p">,</span> <span class="s2">"106.3146732"</span><span class="p">,</span> <span class="s2">"106.9739750"</span><span class="p">]}</span><span class="o">&gt;</span><span class="p">]</span>
</code></pre></div></div>

<h3 id="reverse-search">Reverse Search</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">003</span><span class="o">&gt;</span> <span class="no">Geocoder</span><span class="p">.</span><span class="nf">search</span><span class="p">([</span><span class="o">-</span><span class="mf">1.2379</span><span class="p">,</span> <span class="mf">116.8529</span><span class="p">])</span>
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">D</span><span class="p">,</span> <span class="p">[</span><span class="mi">2026</span><span class="o">-</span><span class="mo">02</span><span class="o">-</span><span class="mi">09</span><span class="no">T00</span><span class="p">:</span><span class="mo">04</span><span class="p">:</span><span class="mf">49.941967</span> <span class="c1">#30316] DEBUG -- : Geocoder: HTTP request being made for http://locahost:8080/reverse?accept-language=en&amp;addressdetails=1&amp;format=json&amp;lat=-1.2379&amp;lon=116.8529</span>
<span class="o">=&gt;</span>
<span class="p">[</span><span class="c1">#&lt;Geocoder::Result::Nominatim:0x0000000125da17b0</span>
  <span class="vi">@cache_hit</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span>
  <span class="vi">@data</span><span class="o">=</span>
   <span class="p">{</span><span class="s2">"place_id"</span><span class="o">=&gt;</span><span class="mi">119266</span><span class="p">,</span>
    <span class="s2">"licence"</span><span class="o">=&gt;</span><span class="s2">"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright"</span><span class="p">,</span>
    <span class="s2">"osm_type"</span><span class="o">=&gt;</span><span class="s2">"way"</span><span class="p">,</span>
    <span class="s2">"osm_id"</span><span class="o">=&gt;</span><span class="mi">109864339</span><span class="p">,</span>
    <span class="s2">"lat"</span><span class="o">=&gt;</span><span class="s2">"-1.2378944"</span><span class="p">,</span>
    <span class="s2">"lon"</span><span class="o">=&gt;</span><span class="s2">"116.8529396"</span><span class="p">,</span>
    <span class="s2">"class"</span><span class="o">=&gt;</span><span class="s2">"highway"</span><span class="p">,</span>
    <span class="s2">"type"</span><span class="o">=&gt;</span><span class="s2">"residential"</span><span class="p">,</span>
    <span class="s2">"place_rank"</span><span class="o">=&gt;</span><span class="mi">26</span><span class="p">,</span>
    <span class="s2">"importance"</span><span class="o">=&gt;</span><span class="mf">0.0533433333333333</span><span class="p">,</span>
    <span class="s2">"addresstype"</span><span class="o">=&gt;</span><span class="s2">"road"</span><span class="p">,</span>
    <span class="s2">"name"</span><span class="o">=&gt;</span><span class="s2">"Jalan Bubutan"</span><span class="p">,</span>
    <span class="s2">"display_name"</span><span class="o">=&gt;</span><span class="s2">"Jalan Bubutan, Gunung Samarinda, Balikpapan Utara, Balikpapan, East Kalimantan, 76125, Indonesia"</span><span class="p">,</span>
    <span class="s2">"address"</span><span class="o">=&gt;</span>
     <span class="p">{</span><span class="s2">"road"</span><span class="o">=&gt;</span><span class="s2">"Jalan Bubutan"</span><span class="p">,</span>
      <span class="s2">"village"</span><span class="o">=&gt;</span><span class="s2">"Gunung Samarinda"</span><span class="p">,</span>
      <span class="s2">"city_district"</span><span class="o">=&gt;</span><span class="s2">"Balikpapan Utara"</span><span class="p">,</span>
      <span class="s2">"city"</span><span class="o">=&gt;</span><span class="s2">"Balikpapan"</span><span class="p">,</span>
      <span class="s2">"state"</span><span class="o">=&gt;</span><span class="s2">"East Kalimantan"</span><span class="p">,</span>
      <span class="s2">"ISO3166-2-lvl4"</span><span class="o">=&gt;</span><span class="s2">"ID-KI"</span><span class="p">,</span>
      <span class="s2">"postcode"</span><span class="o">=&gt;</span><span class="s2">"76125"</span><span class="p">,</span>
      <span class="s2">"country"</span><span class="o">=&gt;</span><span class="s2">"Indonesia"</span><span class="p">,</span>
      <span class="s2">"country_code"</span><span class="o">=&gt;</span><span class="s2">"id"</span><span class="p">},</span>
    <span class="s2">"boundingbox"</span><span class="o">=&gt;</span><span class="p">[</span><span class="s2">"-1.2383814"</span><span class="p">,</span> <span class="s2">"-1.2355025"</span><span class="p">,</span> <span class="s2">"116.8482510"</span><span class="p">,</span> <span class="s2">"116.8530576"</span><span class="p">]}</span><span class="o">&gt;</span><span class="p">]</span>
<span class="n">irb</span><span class="p">(</span><span class="n">main</span><span class="p">):</span><span class="mo">003</span><span class="o">&gt;</span>
</code></pre></div></div>

<h2 id="kesimpulan">Kesimpulan</h2>

<p>Dengan mengikuti langkah-langkah di atas, saya berhasil mengonfigurasi Geocoder gem untuk menggunakan Nominatim self-hosted sebagai geocoding service lookup. Saya juga berhasil melakukan geocoding dan reverse geocoding menggunakan instance Nominatim yang saya jalankan sendiri. Dengan menggunakan Nominatim self-hosted, saya memiliki kontrol penuh atas data geocoding dan dapat menghindari batasan penggunaan yang mungkin diterapkan oleh layanan geocoding pihak ketiga.</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li><a href="https://github.com/alexreisner/geocoder">https://github.com/alexreisner/geocoder</a> <br />
  Diakses tanggal: 2026-02-08</li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="openstreetmap" /><category term="nominatim" /><category term="geocoder" /><category term="geocoding" /><category term="rails" /><summary type="html"><![CDATA[Pada artikel ini, saya akan mencatat langkah-langkah untuk mengonfigurasi Geocoder gem di aplikasi Ruby on Rails agar menggunakan instance Nominatim self-hosted sebagai geocoding service lookup.]]></summary></entry><entry><title type="html">OpenStreetMap Nominatim (Geocoding Machine) dengan Docker</title><link href="https://bandithijo.github.io/blog/openstreetmap-nominatim-dengan-docker" rel="alternate" type="text/html" title="OpenStreetMap Nominatim (Geocoding Machine) dengan Docker" /><published>2026-02-07T11:21:00+08:00</published><updated>2026-02-07T11:21:00+08:00</updated><id>https://bandithijo.github.io/blog/openstreetmap-nominatim-dengan-docker</id><content type="html" xml:base="https://bandithijo.github.io/blog/openstreetmap-nominatim-dengan-docker"><![CDATA[<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-02-07-openstreetmap-nominatim-dengan-docker/gambar_01.png" alt="Gambar 1" /></p>

<h2 id="pendahuluan">Pendahuluan</h2>

<p>OpenStreetMap Nominatim adalah sebuah layanan geocoding yang memungkinkan pengguna untuk mencari lokasi berdasarkan nama tempat  atau alamat (forward search) atau geographic coordinate (reverse search). Dalam artikel ini, saya akan mendokumentasikan cara menjalankan OpenStreetMap Nominatim menggunakan Docker agar proses setup Nominatim menjadi lebih mudah dan cepat, tanpa perlu repot mengatur lingkungan dan dependensi secara manual.</p>

<h2 id="apa-itu-openstreetmap-nominatim">Apa itu OpenStreetMap Nominatim?</h2>

<p>OpenStreetMap Nominatim adalah sebuah layanan geocoding yang memungkinkan pengguna untuk mencari lokasi berdasarkan nama tempat atau alamat (<em>forward search</em>) atau koordinat geografis (<em>reverse search</em>). Nominatim menggunakan data dari OpenStreetMap untuk menyediakan informasi lokasi yang akurat. Tanpa OpenStreetMap, Nominatim tidak memiliki data apapun untuk digunakan dalam proses geocoding.</p>

<p><strong><em>Forward Geocodig</em></strong> adalah proses mengubah nama tempat atau alamat menjadi koordinat geografis (latitude dan longitude). Misalnya, jika mencari “Balikpapan”, Nominatim akan memberikan koordinat geografis dari Balikpapan.</p>

<p><strong><em>Reverse Geocoding</em></strong> adalah proses mengubah koordinat geografis (latitude &amp; longitude) menjadi nama tempat atau alamat. Misalnya, jika memiliki koordinat geografis dari Balikpapan (<code class="language-plaintext highlighter-rouge">-1.2398711,116.8593379</code>), Nominatim akan memberikan nama tempat atau alamat yang sesuai.</p>

<h3 id="nominatim-api">Nominatim API</h3>

<p>Nominatim menyediakan API yang dapat digunakan untuk melakukan forward, reverse geocoding dan beberapa hal lainnya.</p>

<p>Berikut adalah contoh penggunaan Nominatim API:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">/search</code> <br />
  Digunakan untuk melakukan forward geocoding. Untuk mencari lokasi berdasarkan nama tempat atau alamat.</li>
  <li><code class="language-plaintext highlighter-rouge">/reverse</code> <br />
  Digunakan untuk melakukan reverse geocoding. Untuk mencari nama tempat atau alamat berdasarkan koordinat geografis (latitude &amp; longitude).</li>
  <li><code class="language-plaintext highlighter-rouge">/lookup</code> <br />
  Digunakan untuk mencari informasi lokasi berdasarkan OSM ID.</li>
  <li><code class="language-plaintext highlighter-rouge">/details</code> <br />
  Digunakan untuk mencari informasi lokasi berdasarkan OSM ID, dengan informasi yang lebih detail dibandingkan dengan <code class="language-plaintext highlighter-rouge">/lookup</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">/status</code> <br />
  Digunakan untuk memeriksa status dari Nominatim service.</li>
  <li>dll.</li>
</ol>

<h2 id="openstreetmap-nominatim-dengan-docker">OpenStreetMap Nominatim dengan Docker</h2>

<p>Untuk menjalankan OpenStreetMap Nominatim dengan Docker, bisa menggunakan image yang sudah tersedia di Docker Hub. Agar prosesnya lebih mudah, saya prefer untuk membuat docker compose saja. Berikut adalah langkah-langkahnya:</p>

<h3 id="1-buat-docker-compose-untuk-nominatim-service">1. Buat Docker Compose untuk Nominatim Service</h3>

<p>Buat file <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> dengan isi sebagai berikut:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">!filename:</span> <span class="s">docker-compose.yml</span>
<span class="na">services</span><span class="pi">:</span>
 <span class="na">nominatim</span><span class="pi">:</span>
   <span class="na">image</span><span class="pi">:</span> <span class="s">mediagis/nominatim:5.2</span>
   <span class="na">container_name</span><span class="pi">:</span> <span class="s">osm-nominatim</span>
   <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
   <span class="na">environment</span><span class="pi">:</span>
     <span class="na">PBF_PATH</span><span class="pi">:</span> <span class="s">/nominatim/data/kalimantan-latest.osm.pbf</span>
   <span class="na">volumes</span><span class="pi">:</span>
     <span class="pi">-</span> <span class="s">./geofabrik/kalimantan-latest.osm.pbf:/nominatim/data/kalimantan-latest.osm.pbf</span>
     <span class="pi">-</span> <span class="s">nominatim-data:/var/lib/postgresql</span>
   <span class="na">expose</span><span class="pi">:</span>
     <span class="pi">-</span> <span class="s2">"</span><span class="s">8080"</span>
   <span class="na">ports</span><span class="pi">:</span>
     <span class="pi">-</span> <span class="s2">"</span><span class="s">8080:8080"</span>

<span class="na">volumes</span><span class="pi">:</span>
 <span class="na">nominatim-data</span><span class="pi">:</span>
</code></pre></div></div>

<p>Saya menggunakan docker image <code class="language-plaintext highlighter-rouge">mediagis/nominatim:5.2</code> yang sudah tersedia di Docker Hub.</p>

<p>Pada <code class="language-plaintext highlighter-rouge">PBF_PATH</code> diisi dengan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>. PBF adalah format file yang digunakan untuk menyimpan data OpenStreetMap. File ini berisi informasi tentang jalan, bangunan, dan fitur geografis lainnya yang ada di wilayah tertentu. Dalam contoh ini, saya menggunakan file <code class="language-plaintext highlighter-rouge">kalimantan-latest.osm.pbf</code> yang berisi data OpenStreetMap untuk wilayah Kalimantan. Untuk mendapatkan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> untuk wilayah lain, bisa mengunduhnya dari situs <a href="https://download.geofabrik.de/">Geofabrik</a>.</p>

<p>Kisaran size file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> untuk wilayah Indonesia dan beberapa wilayah lainnya adalah sebagai berikut:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">Region</th>
      <th style="text-align: right">Size</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">Indonesia (with East Timor)</td>
      <td style="text-align: right">1.6 GB</td>
    </tr>
    <tr>
      <td style="text-align: left">Java</td>
      <td style="text-align: right">845 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Kalimantan</td>
      <td style="text-align: right">138 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Maluku</td>
      <td style="text-align: right">25.1 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Nusa Tenggara</td>
      <td style="text-align: right">162 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Papua</td>
      <td style="text-align: right">31.0 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Sulawesi</td>
      <td style="text-align: right">149 MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Sumatra</td>
      <td style="text-align: right">255 MB</td>
    </tr>
  </tbody>
</table>

<p>Saya menggunakan 2 values untuk <code class="language-plaintext highlighter-rouge">volumes</code>,</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">.geofabrik/kalimantan-latest.osm.pbf:/nominatim/data/kalimantan-latest.osm.pbf</code> <br />
  Volume ini digunakan untuk menghubungkan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> yang ada di host dengan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> yang ada di dalam container. Dengan begitu, saya dapat menggunakannya di <code class="language-plaintext highlighter-rouge">PBF_PATH</code> untuk proses import data. Proses persiapan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ini akan saya jelaskan di bagian selanjutnya.</li>
  <li><code class="language-plaintext highlighter-rouge">nominatim-data:/var/lib/postgresql</code> <br />
  Volume ini digunakan untuk menyimpan data yang dihasilkan oleh Nominatim. Data ini akan disimpan di dalam container, sehingga jika container dihapus, data ini tidak akan hilang. <code class="language-plaintext highlighter-rouge">nominatim-data</code> ini adalah nama volume yang dibuat dibagian <code class="language-plaintext highlighter-rouge">volumes</code> di bawah <code class="language-plaintext highlighter-rouge">nominatim-data:</code>. Volume ini akan dihubungkan dengan direktori <code class="language-plaintext highlighter-rouge">/var/lib/postgresql</code> di dalam container, yang merupakan direktori tempat Nominatim menyimpan data yang dihasilkan.</li>
</ol>

<p>Secara default nonimatin mengekspose port <code class="language-plaintext highlighter-rouge">8080</code> di dalam container, sehingga saya juga expose port <code class="language-plaintext highlighter-rouge">8080</code> di bagian <code class="language-plaintext highlighter-rouge">expose</code>. Agar bisa diakses dari luar container, saya juga memetakan port <code class="language-plaintext highlighter-rouge">8080</code> di host ke port <code class="language-plaintext highlighter-rouge">8080</code> di dalam container dengan menggunakan <code class="language-plaintext highlighter-rouge">ports</code>.</p>

<h3 id="2-persiapkan-data-osm-berupa-file-osmpbf">2. Persiapkan data OSM berupa file <code class="language-plaintext highlighter-rouge">.osm.pbf</code></h3>

<p>Sebelum menjalankan Docker Compose, pastikan sudah memiliki file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> yang akan digunakan untuk proses import data. File <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ini bisa didapatkan dari situs <a href="https://download.geofabrik.de/">Geofabrik</a>.</p>

<p>Setelah mengunduh file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>, letakkan file tersebut di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code> yang berada di root project. Buat direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code> jika belum ada.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ mkdir geofabrik
$ cd geofabrik
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
</code></pre></div></div>

<p>Tunggu proses download selesai, setelah itu pastikan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> sudah berada di dalam direktori <code class="language-plaintext highlighter-rouge">geofabrik/</code>.</p>

<p>Berikut adalah struktur direktori yang diharapkan setelah menambahkan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>geofabrik/
└── kalimantan-latest.osm.pbf
docker-compose.yml
</code></pre></div></div>

<p>Sip, sekarang file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> sudah siap untuk digunakan dalam proses import data oleh Nominatim.</p>

<h3 id="3-jalankan-docker-compose">3. Jalankan Docker Compose</h3>

<p>Setelah membuat file <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>, jalankan perintah berikut untuk menjalankan Nominatim service:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose up -d
</code></pre></div></div>

<blockquote>
  <p>PERHATIAN</p>

  <p>Untuk pertama kali menjalankan docker compose, Nominatim service akan melakukan proses import data dari file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> ke dalam Nominatim, proses ini membutuhkan waktu yang cukup lama, tergantung pada ukuran file <code class="language-plaintext highlighter-rouge">.osm.pbf</code> yang digunakan. Untuk file <code class="language-plaintext highlighter-rouge">kalimantan-latest.osm.pbf</code> yang berukuran 138 MB, proses import data bisa memakan waktu sekitar 30 menit hingga 1 jam.</p>
</blockquote>

<p>Untuk melihat log dari container, bisa menggunakan perintah berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker compose logs nominatim -f
</code></pre></div></div>

<p>Tunggu proses import data selesai. Biasanya akan muncul log seperti berikut ketika proses import data sudah selesai:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--&gt; Nominatim is ready to accept requests
</code></pre></div></div>

<p>Setelah proses import data selesai, Nominatim service sudah siap untuk digunakan. Nominatim API bisa diakses melalui <code class="language-plaintext highlighter-rouge">http://localhost:8080/</code>.</p>

<h3 id="4-coba-nominatim-api">4. Coba Nominatim API</h3>

<p>Setelah Nominatim service sudah siap, coba akses Nominatim API untuk melakukan forward geocoding dan reverse geocoding.</p>

<h4 id="status">/status</h4>

<p>Pertama-tama coba cek statusnya dulu.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl http://localhost:8080/status
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OK
</code></pre></div></div>

<p>Atau kalau mau outputnya dalam JSON,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl http://localhost:8080/status?format=json
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OK"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"data_updated"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-01-21T12:30:08+00:00"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"software_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.2.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"database_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.2.0-0"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h4 id="search-forward-geocoding">/search (Forward Geocoding)</h4>

<p>Coba lakukan forward geocoding untuk mencari lokasi berdasarkan nama tempat atau alamat. Misalnya, mencari lokasi “jakarta”.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl "http://localhost:8080/search?q=jakarta&amp;format=json"
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"place_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">144282</span><span class="p">,</span><span class="w">
    </span><span class="nl">"licence"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"osm_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"way"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"osm_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">795600162</span><span class="p">,</span><span class="w">
    </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-0.5354329"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"117.0914415"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"highway"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"residential"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"place_rank"</span><span class="p">:</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w">
    </span><span class="nl">"importance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0533433333333333</span><span class="p">,</span><span class="w">
    </span><span class="nl">"addresstype"</span><span class="p">:</span><span class="w"> </span><span class="s2">"road"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"display_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"boundingbox"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="s2">"-0.5355636"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"-0.5353021"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"117.0914346"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"117.0914483"</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>Wah! Ternyata ada daerah beranama “Jakarta” di Kalimantan Timur.</p>

<h4 id="revers-reverse-geocoding">/revers (Reverse Geocoding)</h4>

<p>Coba lakukan reverse geocoding untuk mencari nama tempat atau alamat berdasarkan koordinat geografis (latitude &amp; longitude). Misalnya, mencari lokasi berdasarkan koordinat <code class="language-plaintext highlighter-rouge">-0.5354329,117.0914415</code> (koordinat Balikpapan).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl "http://localhost:8080/reverse?lat=-1.2398711&amp;lon=116.8593379&amp;format=json"
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"place_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">144282</span><span class="p">,</span><span class="w">
  </span><span class="nl">"licence"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"osm_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"way"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"osm_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">795600162</span><span class="p">,</span><span class="w">
  </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-0.5354329"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"117.0914415"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"class"</span><span class="p">:</span><span class="w"> </span><span class="s2">"highway"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"residential"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"place_rank"</span><span class="p">:</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w">
  </span><span class="nl">"importance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0533433333333333</span><span class="p">,</span><span class="w">
  </span><span class="nl">"addresstype"</span><span class="p">:</span><span class="w"> </span><span class="s2">"road"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"display_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"road"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"village"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Loa Bakung"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"city_district"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sungai Kunjang"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Samarinda"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kalimantan Timur"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ISO3166-2-lvl4"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ID-KI"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"postcode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"75391"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"country"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Indonesia"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"country_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"id"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"boundingbox"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"-0.5355636"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"-0.5353021"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"117.0914346"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"117.0914483"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h4 id="lookup">/lookup</h4>

<p>Coba lakukan lookup untuk mencari informasi lokasi berdasarkan OSM ID. Misalnya, mencari informasi lokasi berdasarkan OSM ID <code class="language-plaintext highlighter-rouge">795600162</code>. Ini adalah OSM ID dari hasil pencarian sebelumnya untuk lokasi “jakarta” di Kalimantan Timur.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl "http://localhost:8080/lookup?osm_ids=W795600162&amp;format=json"
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"place_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">144282</span><span class="p">,</span><span class="w">
    </span><span class="nl">"licence"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"osm_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"way"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"osm_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">795600162</span><span class="p">,</span><span class="w">
    </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-0.5354329"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"117.0914415"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"class"</span><span class="p">:</span><span class="w"> </span><span class="s2">"highway"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"residential"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"place_rank"</span><span class="p">:</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w">
    </span><span class="nl">"importance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0533433333333333</span><span class="p">,</span><span class="w">
    </span><span class="nl">"addresstype"</span><span class="p">:</span><span class="w"> </span><span class="s2">"road"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"display_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"road"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"village"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Loa Bakung"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"city_district"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sungai Kunjang"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Samarinda"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kalimantan Timur"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"ISO3166-2-lvl4"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ID-KI"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"postcode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"75391"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"country"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Indonesia"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"country_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"id"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"boundingbox"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="s2">"-0.5355636"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"-0.5353021"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"117.0914346"</span><span class="p">,</span><span class="w">
      </span><span class="s2">"117.0914483"</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<h4 id="details">/details</h4>

<p>Coba lakukan details untuk mencari informasi lokasi berdasarkan OSM ID, dengan informasi yang lebih detail dibandingkan dengan <code class="language-plaintext highlighter-rouge">/lookup</code>. Misalnya, mencari informasi lokasi berdasarkan OSM ID <code class="language-plaintext highlighter-rouge">795600162</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl "http://localhost:8080/details?osmtype=W&amp;osmid=795600162&amp;format=json"
</code></pre></div></div>

<p>Outputnya,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"place_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">144282</span><span class="p">,</span><span class="w">
  </span><span class="nl">"parent_place_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">142133</span><span class="p">,</span><span class="w">
  </span><span class="nl">"osm_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"osm_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">795600162</span><span class="p">,</span><span class="w">
  </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"highway"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"residential"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"admin_level"</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="p">,</span><span class="w">
  </span><span class="nl">"localname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"names"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jakarta"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"addresstags"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w">
  </span><span class="nl">"calculated_postcode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"75391"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"country_code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"id"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"indexed_date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-02-08T13:11:46.748639+00:00"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"importance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0533433333333333</span><span class="p">,</span><span class="w">
  </span><span class="nl">"calculated_importance"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0533433333333333</span><span class="p">,</span><span class="w">
  </span><span class="nl">"extratags"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w">
  </span><span class="nl">"rank_address"</span><span class="p">:</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w">
  </span><span class="nl">"rank_search"</span><span class="p">:</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w">
  </span><span class="nl">"isarea"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"centroid"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Point"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"coordinates"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mf">117.0914415</span><span class="p">,</span><span class="w">
      </span><span class="mf">-0.5354329</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"geometry"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Point"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"coordinates"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mf">117.0914415</span><span class="p">,</span><span class="w">
      </span><span class="mf">-0.5354329</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="5-stop-docker-compose">5. Stop Docker Compose</h3>

<p>Jika sudah selesai menggunakan Nominatim service, bisa stop Docker Compose dengan perintah berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ docker-compose down
</code></pre></div></div>

<h2 id="kesimpulan">Kesimpulan</h2>

<p>Dalam artikel ini, saya telah mendokumentasikan cara menjalankan OpenStreetMap Nominatim menggunakan Docker, mulai dari membuat Docker Compose, mempersiapkan file <code class="language-plaintext highlighter-rouge">.osm.pbf</code>, menjalankan Docker Compose, mencoba Nominatim API, hingga menghentikan Docker Compose. Dengan menggunakan Docker, proses setup Nominatim menjadi lebih mudah dan cepat, tanpa perlu repot mengatur lingkungan dan dependensi secara manual.</p>

<p>Pada artikel selanjutnya, saya akan mendokumentasikan cara menggunakan Nominatim self-hosted pada Geocoder gem untuk melakukan geocoding dan reverse geocoding di aplikasi Ruby on Rails, “<a href="https://bandithijo.github.io/blog/menggunakan-nominatim-self-hosted-pada-geocoder-gem">Menggunakan Nominatim Self-Hosted pada Geocoder Gem</a>”</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://nominatim.openstreetmap.org/ui/about.html">https://nominatim.openstreetmap.org/ui/about.html</a> <br />
  Diakses tanggal: 2026-02-07</p>
  </li>
  <li>
    <p><a href="https://nominatim.org/release-docs/develop/api/Overview/">https://nominatim.org/release-docs/develop/api/Overview/</a> <br />
  Diakses tanggal: 2026-02-07</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="openstreetmap" /><category term="nominatim" /><summary type="html"><![CDATA[OpenStreetMap Nominatim adalah sebuah layanan geocoding yang memungkinkan pengguna untuk mencari lokasi berdasarkan nama tempat atau alamat (forward search) atau geographic coordinate (reverse search). Dalam artikel ini, saya akan mendokumentasikan cara menjalankan OpenStreetMap Nominatim menggunakan Docker agar proses setup Nominatim menjadi lebih mudah dan cepat, tanpa perlu repot mengatur lingkungan dan dependensi secara manual.]]></summary></entry><entry><title type="html">QnA: Fitur Apa yang Dimiliki Rails Sehingga Sulit untuk Pindah ke Framework Lain?</title><link href="https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain" rel="alternate" type="text/html" title="QnA: Fitur Apa yang Dimiliki Rails Sehingga Sulit untuk Pindah ke Framework Lain?" /><published>2026-02-01T18:13:00+08:00</published><updated>2026-02-01T18:13:00+08:00</updated><id>https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain</id><content type="html" xml:base="https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain"><![CDATA[<h2 id="pendahuluan">Pendahuluan</h2>

<p>QnA ini di-request oleh mas Rizky Ramadhan via email. Pertanyaan ini diajukan sekitar tahun 2023. Ia bertanya sebagian besar terkait dengan Ruby on Rails. Saya merasa pertanyaan dan jawaban ini akan memberikan lebih banyak manfaat jika dipublikasikan. Semoga bermanfaat yaa.</p>

<blockquote>
  <p>PERHATIAN</p>

  <p>Saya kesulitan mencari alamat email dari mas Rizky Ramadhan, jika mas Rizky membaca ini dan tidak berkenan untuk mempublikasikan tulisan ini, boleh hubungi saya via email yaa.</p>
</blockquote>

<h2 id="question-from-rizky-ramadhan">Question: from Rizky Ramadhan</h2>

<p>Halo, mas. Perkenalkan, saya Rizky, baru lulus tahun ini, sekarang sudah bekerja, sekitar 1 bulan lebih dikit. Kalau di kantor biasa pakai Yii, untuk hobi saya suka Laravel. Mau tanya soal Ruby on Rails.</p>

<p>Kemarin saya sudah menonton tutorial Ruby on Rails dari Padang Tekno di YouTube, lumayan sulit juga nyari tutorial yang up to date, kebanyakan 3 tahun / 5 tahun yang lalu.</p>

<p>Kalau dari yang saya pahami, konsep Rails dan Laravel lumayan mirip. Apa yang bisa dilakukan Rails, bisa juga dilakukan Laravel. Meskipun, kalau soal syntax, Ruby lebih elegan.</p>

<p>Jadi, pertanyaannya:</p>

<ol>
  <li><a href="https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain#q-apa-yang-bikin-bertahan-dengan-rails-sampai-hari-ini"><strong>Apa yang bikin bertahan dengan Rails sampai hari ini mas?</strong></a></li>
  <li><a href="https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain#q-kelebihan-apa-yang-dimiliki-rails-sehingga-memudahkan-pada-saat-bekerja"><strong>Kelebihan apa yang dimiliki Rails sehingga memudahkan pada saat bekerja?</strong></a></li>
  <li><a href="https://bandithijo.github.io/blog/qna-fitur-yang-dimilikirails-sehingga-sulit-pindah-ke-framework-lain#q-fitur-apa-yang-dimiliki-rails-sehingga-sulit-untuk-pindah-ke-framework-lain"><strong>Fitur apa yang dimiliki Rails sehingga sulit untuk pindah ke framework lain?</strong></a></li>
</ol>

<p>Saya tertarik buat nyoba Rails, mas. Cuman, belum click di mana “magic” nya, selain syntax yang elegan tadi.</p>

<p>Udah liat-liat comparison nya di internet, tapi sepertinya kebih baik bertanya langsung kepada sepuh. Makasih mas 🙏</p>

<h2 id="answer-from-bandithijo">Answer: from BanditHijo</h2>

<p>Halo halo Rizky.</p>

<p>Selamat atas kelulusan dan sudah keterima bekerja. Mudah-mudahan berkah ilmunya yaa.</p>

<p>Sebelumnya, terima kasih yaa sudah mengajukan pertanyaan terkait Rails. Sebenernya saya belum bisa disebut sepuh Rails, karena baru pakai Rails dari tahun 2019-2021 (FullStack), 2022-2023 (Backend). Wkwkwk.</p>

<h3 id="q-apa-yang-bikin-bertahan-dengan-rails-sampai-hari-ini">Q: Apa yang bikin bertahan dengan Rails sampai hari ini?</h3>

<p>Mungkin karena masih bekerja pakai Rails dan belum ketemu kesulitan dan jalan buntu ketika digunakan saat bekerja. Masih happy pakai Rails.</p>

<h3 id="q-kelebihan-apa-yang-dimiliki-rails-sehingga-memudahkan-pada-saat-bekerja">Q: Kelebihan apa yang dimiliki Rails sehingga memudahkan pada saat bekerja?</h3>

<p>Kelebihan-kelebihan yang saya rasakan ketika pakai Rails saat bekerja:</p>

<ol>
  <li>
    <p>Rails ini framework yang bersifat Opinionated. Artinya sudah ada aturan yang disepakati (secara umum) untuk melakukan suatu pekerjaan yang umum (biasa disebut Best Practice). Kalau bekerja dalam tim, sifat opinionated ini yang mempermudah dan mempercepat menyelesaikan suatu task. Karena kita tidak perlu lagi memperdebatkan mana cara penyelesaian masalah yang baik. Kecuali ada kondisi-kondisi tertentu yang disepakai oleh tim untuk tidak menggunakan “best practice” karena kebutuhan tertentu.</p>
  </li>
  <li>
    <p>Proses menyelesaikan task menjadi lebih cepat karena beberapa flow bisnis, sudah ada dan sudah dibuatkan best practicenya, jadi tidak perlu lagi memperdebatkan flow bisnis yang sudah umum. Tinggal pakai saja kalau memang tidak ada kebutuhan khusus.</p>

    <p>Dengan begini, perdebatan terkait hal-hal teknis, yang umumnya terjadi di tempat kami:</p>

    <ul>
      <li><strong>Nama tabel</strong> di Rails, sudah dikonvensikan kalau nama tabel harus kata benda yang plural</li>
      <li><strong>Penggunaan HTTP method</strong> harus mengikuti aturan RESTful API. Dimana ketika membuat endpoint harus menggunakan HTTP method yang sesuai untuk fungsi yang sesuai (GET/POST/PATCH/PUT/DELETE).</li>
    </ul>
  </li>
  <li>
    <p>Metaprogramming. Ruby memiliki kemampuan metaprogramming yang diadopsi oleh Rails untuk mempermudah proses pembuatan aplikasi.</p>

    <p>Terkait metraprogramming di Rails, ada banyak sekali, tapi yang baru-baru aja (inget) saya pakai yang seperti ini:</p>

    <ul>
      <li>
        <p><strong>Active Model Dirty</strong> <a href="https://api.rubyonrails.org/classes/ActiveModel/Dirty.html">https://api.rubyonrails.org/classes/ActiveModel/Dirty.html</a> <br />
Saya pakai untuk membuat callback, ketika suatu atribute (misal: name) berubah nilainya.</p>
      </li>
      <li>
        <p><strong>Active Record Nested Attributes</strong> <a href="https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html">https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html</a> <br />
Saya pakai untuk membuat data child yang berasosiasi dengan parent. Jadi ketika parentnya dibuat, child nya juga ikut terbut. Misal: membuat admin (parent) baru dengan beberapa admin_permission (child).</p>
      </li>
    </ul>

    <p>2 method di atas, terdengar biasa aja. Tapi kalau dilihat kodenya, hal-hal tersebut dapat dilakukan hanya dengan kode yang simple tanpa perlu banyak effort.</p>

    <p>Hal lain seperti migrasi dan relasi tabel juga pakai metaprogramming, sehingga ada method-method yang secara ajaib sudah ada dan tinggal dipakai.</p>

    <p>Kebetulan kemarin sempet lihat topik terkait “Powerful Rails Feature You Might Not Know” dari Chris Oliver di Rails World 2023, bisa coba tonton di sini yaa <a href="https://www.youtube.com/watch?v=iPCqwZ9Ouc4">https://www.youtube.com/watch?v=iPCqwZ9Ouc4</a></p>
  </li>
  <li>
    <p>Dokumentasi yang dimiliki Rails juga terbilang rapi dan deskriptif. Mulai dari dokumentasi Rails yang ada di <a href="https://guides.rubyonrails.org/">https://guides.rubyonrails.org/</a>, sampai dokumentasi Rails API yang ada di <a href="https://api.rubyonrails.org/">https://api.rubyonrails.org/</a></p>
  </li>
  <li>
    <p>Rails sebenarnya kumpulan dari beberapa module (library) yang modular yang didesain untuk memiliki intarface yang agnostic. Jadi bisa pakai engine (backend) apa saja. Misal, untuk Active Job kita bisa pakai Sidekiq, Resque, Sneakers, Delayed Job, Good Job, dll. <a href="https://edgeguides.rubyonrails.org/active_job_basics.html#starting-the-backend">https://edgeguides.rubyonrails.org/active_job_basics.html#starting-the-backend</a> Tapi dengan interface yang sama. Jadi meskipun backend job nya bisa berbeda, tapi interface nya tetep sama (nama pemanggilan method nya sama). Terkait Rails dan modularitynya, bisa tonton dari salah satu Rails Core, Eileen Uchitelle di Keynote RailsConf 2023 <a href="https://www.youtube.com/watch?v=TKulocPqV38">https://www.youtube.com/watch?v=TKulocPqV38</a>.</p>
  </li>
</ol>

<h3 id="q-fitur-apa-yang-dimiliki-rails-sehingga-sulit-untuk-pindah-ke-framework-lain">Q: Fitur apa yang dimiliki Rails sehingga sulit untuk pindah ke framework lain?</h3>

<p>Jujurly saya belum pernah coba framework lain selama Rails, jadi belum bisa kasih jawaban.</p>

<p>Rails ini framework yang sudah lama, sudah mature dalam artian sudah banyak digunakan untuk menyelesaikan beberapa bisnis flow. Yang kemudian flow tersebut dibakukan dalam bentuk method, sehingga tinggal kita pakai dan panggil saja. Tentunya ketersediaan built-in class dan method ini dan hal-hal yang berkaitan dengan jawaban sebelumnya juga yang membuat proses penyelesaian suatu problem menjadi lebih cepat.</p>

<p>Kira-kira ini sedikit yang bisa saya sharing terkait Rails dari pengalaman selama menggunakan Rails untuk kerjaan.</p>

<p>Untuk sekarang, dikerjaan, saya lebih banyak pakai Rails sebagai Backend saja. Jadi tidak menjadikan Rails sebagai FullStack. Maka dari itu, jawaban dan ilustrasi yang kuberikan, terbatas di Rails sebagai Backend (sudut pandang fitur-fitur yang memudahkan pekerjaan di Backend). Tentunya tidak sebanding dengan orang yang menjadikan Rails sebagai Fullstack. Karena di bagian Frontend juga terdapat method-method helper yang banyak memudahkan untuk mempercepat proses pembuatan tampilan web.</p>

<p>Untuk kedepannya, saya juga masih sambil belajar JavaScript. Pengetahuan JavaScript sebagai Rails Developer tentu akan sangat membantu, dibandingkan dengan Rails Developer yang tidak memiliki pengetahuan tentang JavaScript sama sekali.</p>

<p>Mudah-mudahan jawaban saya sudah menjawab 3 pertanyaan yang diajukan yaa.</p>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[QnA ini di-request oleh mas Rizky Ramadhan via email. Pertanyaan ini diajukan sekitar tahun 2023. Ia bertanya sebagian besar terkait dengan Ruby on Rails. Saya merasa pertanyaan dan jawaban ini akan memberikan lebih banyak manfaat jika dipublikasikan. Semoga bermanfaat yaa.]]></summary></entry><entry><title type="html">Berkenalan dengan AsciiDoc</title><link href="https://bandithijo.github.io/blog/berkenalan-dengan-asciidoc" rel="alternate" type="text/html" title="Berkenalan dengan AsciiDoc" /><published>2026-01-18T12:37:00+08:00</published><updated>2026-01-18T12:37:00+08:00</updated><id>https://bandithijo.github.io/blog/berkenalan-dengan-asciidoc</id><content type="html" xml:base="https://bandithijo.github.io/blog/berkenalan-dengan-asciidoc"><![CDATA[<h2 id="apa-itu-asciidoc">Apa itu AsciiDoc?</h2>

<p>AsciiDoc adalah bahasa markup teks ringan yang digunakan untuk menulis dokumentasi, artikel, buku, dan konten lainnya. Mirip dengan Markdown, AsciiDoc memungkinkan penulis untuk membuat dokumen yang mudah dibaca dan ditulis dalam format teks biasa, yang kemudian dapat dikonversi ke berbagai format output seperti HTML, PDF, dan ePub.</p>

<blockquote>
  <p>INFO</p>

  <p>“<em>AsciiDoc is a plain text markup language for writing technical content. It’s packed with semantic elements and equipped with features to modularize and reuse content. AsciiDoc content can be composed using a text editor, managed in a version control system, and published to multiple output formats.</em>” - AsciiDoc.org</p>
</blockquote>

<h2 id="awal-mula-bertemu-asciidoc">Awal Mula Bertemu AsciiDoc</h2>

<p>Awal mula saya bertemu dengan Markup Language ini adalah ketika saya membeli sebuah ebook berjudul “Pemrogram Rp 100 Juta: Panduan Bagi Pemrogram Untuk Menggapai Harga, Tahta, dan Kemasyhuran” oleh Arjuna Sky Kok pada tahun 2020.</p>

<p>Saat itu kami diberikan akses ke private repositori GitHub tempat buku tersebut disimpan, dan saya menemukan bahwa buku tersebut ditulis menggunakan AsciiDoc. Saat itu saya berfikir, “Wah! Keren sekali bisa membuat sebuah dokumen atau buku menggunakan bahasa markup seperti ini.” – Saat itu saya belum mengenal LaTeX. Karena yang saya tahu, untuk membuat dokumen atau buku perlu menggunakan aplikasi pengolah kata seperti Microsoft Word atau LibreOffice Writer atau dengan aplikasi desktop publishing seperti Adobe InDesign atau Scribus.</p>

<p>Dikemudian hari, ternyata saya cukup sering menemukan proyek-proyek open source yang menggunakan AsciiDoc untuk dokumentasi mereka, seperti proyek-proyek yang menggunakan Asciidoctor sebagai tool untuk mengkonversi dokumen AsciiDoc ke format lain. Beberapa diantaranya seperti:</p>

<ol>
  <li><a href="https://github.com/madeindjs/api_on_rails">GitHub: Book: Api on Rails 6</a> (AsciiDoc + Asciidoctor)</li>
  <li><a href="https://gitlab.com/fedora/docs/fedora-linux-documentation/release-docs-home">FedoraProject Docs: GitLab: Fedora Linux Documentation Home</a> (AsciiDoc + Antora + Asciidoctor)</li>
</ol>

<blockquote>
  <p>PERTANYAAN</p>

  <p><strong>Apa perbedaan AsciiDoc dan Asciidoctor?</strong></p>

  <p>AsciiDoc adalah markup language yang digunakan untuk menulis dokumen, sedangkan Asciidoctor adalah tool atau perangkat lunak yang digunakan untuk mengkonversi dokumen AsciiDoc ke berbagai format output seperti HTML, PDF, ePub, dan lainnya.</p>
</blockquote>

<h2 id="apakah-tidak-cukup-dengan-markdown">Apakah tidak cukup dengan Markdown?</h2>

<p>Markdown memang sudah sangat populer dan banyak digunakan untuk menulis dokumentasi, artikel, dan konten lainnya. Namun, AsciiDoc menawarkan beberapa kelebihan dibandingkan Markdown, terutama untuk dokumentasi teknis yang kompleks. Beberapa kelebihan AsciiDoc dibandingkan Markdown antara lain:</p>

<ol>
  <li><strong>Fitur yang Lebih Lengkap</strong> <br />
  AsciiDoc memiliki fitur yang lebih lengkap dibandingkan Markdown, seperti dukungan untuk tabel, blok kode dengan penyorotan sintaks, catatan kaki, dan lainnya.</li>
  <li><strong>Mudah Dibaca dan Ditulis</strong> <br />
  AsciiDoc dirancang agar mudah dibaca dalam format teks biasa, sehingga penulis dapat fokus pada konten tanpa terganggu oleh format yang rumit.</li>
  <li><strong>Fleksibilitas Format Output</strong> <br />
  AsciiDoc dapat dikonversi ke berbagai format output seperti HTML, PDF, ePub, dan lainnya menggunakan tool seperti Asciidoctor.</li>
  <li><strong>Dukungan untuk Modularisasi Konten</strong> <br />
  AsciiDoc mendukung modularisasi konten, sehingga penulis dapat mengelola dokumen yang besar dengan lebih efisien.</li>
  <li><strong>Integrasi dengan Development Tool</strong> <br />
  AsciiDoc sering digunakan dalam proyek perangkat lunak untuk dokumentasi, dan dapat diintegrasikan dengan development tool seperti Git dan CI/CD.</li>
</ol>

<h2 id="contoh-dokumen-asciidoc">Contoh Dokumen AsciiDoc</h2>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>= Title of the document
Rizqi Nur Assyaufi <span class="nv">&lt;bandithijo@example.mail&gt;</span>
:source-highlighter: rouge
:icons: font
:toc: macro
:toc-title:

== Table of contents

toc::[]

</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_01.png" alt="Gambar 1" /></p>

<p>Gambar 1. Table of Contents dan Judul Dokumen</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>== Section

Hi, my name is {author}. I'm a Backend Software Engineer at Small Startup footnote:[Startup is a new company, often technology-focused, designed for rapid growth by validating a scalable business model, seeking external funding (like venture capital), and aiming to disrupt existing markets with innovative products or services — Wikipedia] in Jakarta that are working remotely from Balikpapan. Feel free to ask me anything about Software Develop-thing or Linux-thing on {email}.

<span class="ge">*This is bold text*</span>

<span class="ge">_This is cursive text_</span>

<span class="sb">`This is mono text`</span>

</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_02.png" alt="Gambar 2" /></p>

<p>Gambar 2. Paragraf dengan format teks</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>== List

Docker allows to package the full stack in a container:
<span class="p">
*</span> OS
<span class="ge">**</span> Windows
<span class="ge">**</span> Ubuntu
<span class="p">*</span> JVM,
<span class="p">*</span> App server
<span class="p">*</span> Application with its configuration
</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_03.png" alt="Gambar 3" /></p>

<p>Gambar 3. Nested list</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>== Block quotes

[quote, Ben Parker, Spiderman Movie]
<span class="gs">____</span>
With great power comes great responsibility.
<span class="gs">____</span>

</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_04.png" alt="Gambar 4" /></p>

<p>Gambar 4. Block quote</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
== Defining cross references

You can assign meta data to a block. Here’s an example of a quote block that includes all types of metadata:

<span class="gh">[source, ruby, linenums]
----
</span>class Cat <span class="nt">&lt;</span><span class="err">1</span><span class="nt">&gt;</span>
  def meow <span class="nt">&lt;</span><span class="err">2</span><span class="nt">&gt;</span>
    puts "Meow!" <span class="nt">&lt;</span><span class="err">3</span><span class="nt">&gt;</span>
  end
end

cat = Cat.new <span class="nt">&lt;</span><span class="err">4</span><span class="nt">&gt;</span>
<span class="gh">cat.meow &lt;5&gt;
----
</span><span class="nt">&lt;</span><span class="err">1</span><span class="nt">&gt;</span> class definition
<span class="nt">&lt;</span><span class="err">2</span><span class="nt">&gt;</span> method definition
<span class="nt">&lt;</span><span class="err">3</span><span class="nt">&gt;</span> output statement
<span class="nt">&lt;</span><span class="err">4</span><span class="nt">&gt;</span> creating an instance of the class
<span class="nt">&lt;</span><span class="err">5</span><span class="nt">&gt;</span> calling the method
</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_05.png" alt="Gambar 5" /></p>

<p>Gambar 5. Cross reference in code block</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>== Admonition paragraphs and blocks for warnings, notes and tips

An admonition paragraph draws the reader’s attention to certain information. It can be defined by a predefined label at the beginning of the paragraph or as a block.

Here are the other built-in admonition types:

NOTE: Some additional info...

TIP: Pro tip...

IMPORTANT: Don't forget...

WARNING: Watch out for...

CAUTION: Ensure that...

</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_06.png" alt="Gambar 6" /></p>

<p>Gambar 6. Admonition blocks</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hi, my name is {author}. I'm a Backend Software Engineer at Small Startup footnote:[Startup is a new company, often technology-focused, designed for rapid growth by validating a scalable business model, seeking external funding (like venture capital), and aiming to disrupt existing markets with innovative products or services — Wikipedia] in Jakarta that are working remotely from Balikpapan. Feel free to ask me anything about Software Develop-thing or Linux-thing on {email}.
</code></pre></div></div>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_07.png" alt="Gambar 7" /></p>

<p>Gambar 7. Footnote example</p>

<p>Lalu dengan menggunakan tool Asciidoctor, dokumen tersebut dapat dieksport ke dalam format PDF.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ asciidoctor-pdf sample_document.adoc
</code></pre></div></div>

<p>Maka akan menghasilkan file <code class="language-plaintext highlighter-rouge">sample_document.pdf</code> yang berisi dokumen yang sudah terformat dengan baik.</p>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_08.png" alt="Gambar 8" /></p>

<p>Gambar 8. Hasil eksport dokumen ke format PDF halaman 1</p>

<p><img src="/assets/posts/blog/2026/2026-01-18-berkenalan-dengan-asciidoc/gambar_09.png" alt="Gambar 9" /></p>

<p>Gambar 9. Hasil eksport dokumen ke format PDF halaman 2</p>

<p>Dapat dilihat dari contoh-contoh di atas, dokumen yang dihasilkan memiliki format yang rapi dan terlihat profesional.</p>

<p>Sintaks AsciiDoc juga mendukung berbagai elemen seperti judul, paragraf, list, blok kode, quote, cross reference, footnote, table of contents, dan blok peringatan (admonition blocks) untuk menyoroti informasi penting.</p>

<p>Dokumen di atas hanyalah contoh sederhana dari apa yang dapat dilakukan dengan AsciiDoc. Untuk membuat menjadi layout buku juga tinggal menggunakan dokumen type book.</p>

<h2 id="komentar-penulis">Komentar Penulis</h2>

<p>Bukan saya alergi dengan WYSIWYG editor seperti Ms.Word atau LO Writer, tapi “menurut keyakinan saya”, menulis dokumen teknis lebih efisien dengan AsciiDoc. Kita tinggal mengisi konten saja dengan notasi-notasi (sintaks) yang sesuai, processor yang akan merender menjadi format yang sesuai. Tidak perlu formatting text manual.</p>

<p>Kalau membuat dokumen teknis itu 50% nya menulis konten dan 50% nya lagi formatting text. Maka dengan AsciiDoc, kita bisa 90% fokus menulis kontent dan 10% nya konfigurasi template awal.</p>

<p>Bahkan jika konfigurasi templatenya sudah jadi, “menurut keyakinan saya” malah bisa 100% tinggal menulis konten saja.</p>

<h2 id="penutup">Penutup</h2>

<p>Dengan fitur-fitur yang lengkap dan fleksibilitasnya, AsciiDoc menjadi pilihan yang menarik untuk menulis dokumentasi teknis.</p>

<p>Untuk informasi lebih lanjut tentang AsciiDoc, Anda dapat mengunjungi situs resminya atau dokumentasi Asciidoctor yang saya sertakan pada bagian referensi di bawah.</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://asciidoc.org/">https://asciidoc.org/</a> <br />
  Tanggal diakses: 2026-01-18</p>
  </li>
  <li>
    <p><a href="https://docs.asciidoctor.org/asciidoc/latest/">https://docs.asciidoctor.org/asciidoc/latest/</a> <br />
  Tanggal diakses: 2026-01-18</p>
  </li>
  <li>
    <p><a href="https://asciidoctor.org/">https://asciidoctor.org/</a> <br />
  Tanggal diakses: 2026-01-18</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="asciidoc" /><summary type="html"><![CDATA[AsciiDoc adalah bahasa markup teks ringan yang digunakan untuk menulis dokumentasi, artikel, buku, dan konten lainnya. Mirip dengan Markdown, AsciiDoc memungkinkan penulis untuk membuat dokumen yang mudah dibaca dan ditulis dalam format teks biasa, yang kemudian dapat dikonversi ke berbagai format output seperti HTML, PDF, dan ePub.]]></summary></entry><entry><title type="html">Apa yang Baru di Blog BanditHijo versi 3?</title><link href="https://bandithijo.github.io/blog/apa-yang-baru-di-blog-bandithijo-versi-3" rel="alternate" type="text/html" title="Apa yang Baru di Blog BanditHijo versi 3?" /><published>2026-01-16T10:06:00+08:00</published><updated>2026-01-16T10:06:00+08:00</updated><id>https://bandithijo.github.io/blog/apa-yang-baru-di-blog-bandithijo-versi-3</id><content type="html" xml:base="https://bandithijo.github.io/blog/apa-yang-baru-di-blog-bandithijo-versi-3"><![CDATA[<h2 id="pendahuluan">Pendahuluan</h2>

<p>Di pertengahan tahun 2025, saya memutuskan untuk membuat perubahan besar pada cara saya menulis artikel di blog ini. Setelah beberapa tahun menggunakan berbagai notasi syntax, saya merasa perlu untuk menyederhanakan proses penulisan agar tetap mempertahankan mood dalam menulis. Oleh karena itu, pada BanditHijo Versi 3, saya berfokus pada penggunaan CommonMark specification sebagai standar penulisan artikel.</p>

<h2 id="format-penulisan-dari-versi-ke-versi">Format Penulisan dari Versi ke Versi</h2>

<p>Sedikit kilas balik, pada versi pertama blog ini [1], saya menggunakan modifikasi tag/notasi markdown yang cukup kompleks untuk menulis artikel. Hal ini membuat proses penulisan menjadi lebih rumit dan terkadang mengganggu alur kreatif saya.</p>

<h3 id="versi-pertama-markdown--jekyll-custom-tags">Versi Pertama (Markdown + Jekyll Custom Tags)</h3>

<p><img src="/assets/posts/blog/2026/2026-01-16-apa-yang-baru-di-blog-bandithijo-versi-3/gambar_01.png" alt="Gambar 1" /></p>

<p>Gambar 1. BanditHijo’s Blog versi pertama</p>

<p>Sebagai gambaran, seperti ini Jekyll custom tag yang saya gunakan pada versi pertama:</p>

<ol>
  <li>Jika ingin menuliskan shell dengan user prompt:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% shell_user %}
sudo pacman -Syu
{% endshell_user %}
</code></pre></div>    </div>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo pacman -Syu
</code></pre></div>    </div>
  </li>
  <li>Jika ingin menuliskan shell dengan custom prompt:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% shell_term postgres&gt; %}
CONNECT my_database;
{% endshell_term %}
</code></pre></div>    </div>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>postgres&gt; CONNECT my_database;
</code></pre></div>    </div>
  </li>
  <li>Jika ingin memasukkan gambar:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% image https://image.url/123/image_01.png | 1 %}
</code></pre></div>    </div>
  </li>
  <li>Jika ingin memasukkan gambar dengan caption:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% image https://image.url/123/image_01.png | 1 | Caption di sini %}
</code></pre></div>    </div>
  </li>
</ol>

<p>Dan masih banyak lagi notasi khusus yang saya pergunakan pada versi pertama. Artikel lengkapnya bisa di baca di sini: <a href="/blog/membuat-jekyll-custom-tags-dengan-liquid-tags">Membuat Jekyll Custom Tags dengan Liquid Tags</a>.</p>

<p>Pada mulanya, saya merasa bahwa dengan menggunakan custom tag/notasi markdown tersebut, saya dapat menulis artikel dengan lebih ekspresif. Namun, seiring berjalannya waktu, saya menyadari bahwa kompleksitas tersebut justru menghambat proses penulisan saya.</p>

<h3 id="versi-kedua-asciidoc">Versi Kedua (AsciiDoc)</h3>

<p>Saya juga sempat mencoba merubah format artikel di versi kedua [2], yang tadinya menggunakan Markdown pada versi pertama, saya ubah menjadi AsciiDoc. Pertimbangan saya menggunakan AsciiDoc, karena blog saya sebagian besar berisi hal-hal teknis. Sehingga cocok menggunakan notasi AsciiDoc yang luwes untuk dokumen teknis.</p>

<p><img src="/assets/posts/blog/2026/2026-01-16-apa-yang-baru-di-blog-bandithijo-versi-3/gambar_02.png" alt="Gambar 2" /></p>

<p>Gambar 2. BanditHijo’s Blog versi kedua</p>

<p>Namun, setelah mencoba beberapa waktu, dan memigrasikan beberapa artikel saya, proses build menjadi lebih lambat dan proses menulis menggunakan AsciiDoc ternyata tidak semudah yang saya bayangkan. Akhirnya, saya memutuskan untuk kembali lagi menggunakan versi pertama.</p>

<p>AsciiDoc memang memiliki kelebihan dalam hal penulisan dokumen teknis, namun untuk blog pribadi yang lebih banyak berisi artikel ringan dan tutorial, saya merasa bahwa Markdown lebih sesuai dengan kebutuhan saya. Tapi, saya masih tetap menggunakan AsciiDoc untuk membuat dokumentasi teknis yang saya berikan untuk pihak lain. Karena membuat dokumentasi teknis dengan AsciiDoc sangat efisien. Karena hanya cukup menyediakan template dokumen, kemudian tinggal menulis kontennya saya, lalu build menjadi dokumen.</p>

<p>Saya menggunakan AsciiDoctor sebagai tool untuk formatingnya, dan AsciiDoctor PDF untuk merubah menjadi format PDF. Keduanya adalah tool yang sangat powerful untuk membuat dokumentasi teknis.</p>

<p>AsciiDoc menurut saya levelnya berada di antara Markdown dan LaTeX. Jika Markdown terlalu sederhana, dan LaTeX terlalu kompleks, maka AsciiDoc adalah solusi di tengah-tengahnya.</p>

<h3 id="versi-ketiga-commonmark-specification">Versi Ketiga (CommonMark Specification)</h3>

<p>Seiring berjalannya waktu, saya menyadari bahwa semakin sederhana proses penulisan, semakin bersemangat saya untuk menulis. Karena custom tag/notasi markdown yang saya gunakan pada versi pertama justru membuat proses menulis menjadi lebih rumit dan mengganggu alur kreatif saya. Meskipun saya sudah menggunakan snippet code di Neovim untuk mempercepat penulisan, tetap saja prosesnya terasa kurang praktis buat saya.</p>

<p>Maka dari itu, di pertengahan tahun 2025, saya memutuskan untuk membuat perubahan besar pada cara saya menulis artikel di blog ini. Setelah beberapa tahun menggunakan berbagai notasi syntax, saya merasa perlu untuk menyederhanakan proses penulisan agar tetap mempertahankan mood dalam menulis. Oleh karena itu, pada BanditHijo Versi 3, saya berfokus pada penggunaan CommonMark specification sebagai standar penulisan artikel.</p>

<p>Ada beberapa keuntungan yang saya dapatkan dengan mengikuti CommonMark specification:</p>

<ol>
  <li><strong>Sederhana dan Efisien</strong>: Proses penulisan menjadi lebih cepat dan efisien karena saya tidak perlu mengingat berbagai custom tag/notasi markdown yang rumit.</li>
  <li><strong>Konsistensi</strong>: Artikel-artikel di blog ini menjadi lebih konsisten dalam formatnya, sehingga pembaca dapat dengan mudah memahami konten tanpa terganggu oleh variasi notasi.</li>
  <li><strong>Fokus pada Konten</strong>: Saya dapat lebih fokus pada isi artikel tanpa harus terganggu oleh aspek teknis penulisan.</li>
  <li><strong>Kompatibilitas</strong>: Dengan mengikuti standar yang umum digunakan, artikel-artikel di blog ini lebih mudah diakses dan dibaca di berbagai platform dan perangkat. Misalnya, jika saya ingin memigrasikan blog ini ke platform lain di masa depan, prosesnya akan lebih mudah karena artikel-artikel sudah mengikuti standar yang umum. Selain itu juga jadi lebih kompatibel dengan berbagai tool yang mendukung Markdown seperti saat dibaca oleh RSS reader, atau saat diimport ke aplikasi note-taking seperti Obsidian, Notion, dsb.</li>
</ol>

<p>Untuk menjaga konsistensi format penulisan, saya mendokumentasikan rules dalam menulis di sini “<a href="/writing-rules">Writing Rules</a>” yang berisi panduan menulis artikel di blog ini.</p>

<p>Saya berusaha semaksimal mungkin mengikuti CommonMark specification dan seminimal mungkin menggunakan modifikasi tag/notasi markdown dalam menulis artikel.</p>

<h2 id="thema-baru-di-blog-bandithijo-versi-3">Thema Baru di Blog BanditHijo versi 3</h2>

<p>Saya menggunakan thema baru yang lebih minimalis dan fokus pada konten artikel.</p>

<p>Kali ini saya tidak lagi menggunakan plain CSS yang dibuat sendiri seperti pada versi sebelumnya, melainkan menggunakan framework CSS bernama <a href="https://tailwindcss.com/">Tailwind CSS</a>. Framework ini memungkinkan saya untuk membuat desain yang responsif dan modern dengan lebih mudah.</p>

<p><img src="/assets/posts/blog/2026/2026-01-16-apa-yang-baru-di-blog-bandithijo-versi-3/gambar_03.png" alt="Gambar 3" /></p>

<p>Gambar 3. BanditHijo’s Blog versi ketiga</p>

<h2 id="assets-management">Assets Management</h2>

<p>Sebelumnya, pada versi pertama, saya menyimpan semua gambar di layanan pihak ketiga, seperti postimages.org. Pada mulanya, saya pikir gambar akan membuat repo blog ini menjadi gemuk dan sulit di-manage. Namun, setelah mempertimbangkan berbagai aspek, saya memutuskan untuk menyimpan semua gambar langsung di dalam repository blog ini. Ceritanya bisa dibaca di sini, <a href="/blog/preferensi-saya-dalam-menyimpan-assets-gambar-di-jekyll">Preferensi Saya dalam Menyimpan Assets Gambar di Jekyll</a>.</p>

<p>Untuk manajemen assets seperti gambar, saya membuat struktur direktori yang lebih terorganisir di dalam repository blog ini. Setiap artikel memiliki direktori khusus untuk menyimpan gambar dan file terkait lainnya. Hal ini memudahkan saya dalam mengelola assets baik berupa gambar maupun file lainnya.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>📂 _posts/
└ 📂 blog/
  └ 📂 2025/
    └ 📄 2025-12-29-ruby-programmers-best-friend.md 👈 artikel
📂 assets/
│ 📁 css/
│ 📂 images/
└ 📂 posts/
  └ 📂 blog/
    └ 📂 2025/
      └ 📂 2025-12-29-ruby-programmers-best-friend/ 👈 direktori assets
        │ 📄 file-01.pdf
        │ 📄 gambar-01.png
        └ 📄 gambar-02.png
⚙️ _config.yml
📄 index.markdown
📄 README.md
</code></pre></div></div>

<p>Kemudian pada front matter artikel, saya menambahkan variabel <code class="language-plaintext highlighter-rouge">assets</code> yang berisi path ke direktori assets artikel tersebut. Sehingga saat menulis artikel, saya hanya perlu merujuk ke variabel <code class="language-plaintext highlighter-rouge">assets</code> untuk menyisipkan gambar atau file lainnya.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">layout</span><span class="pi">:</span> <span class="s2">"</span><span class="s">post"</span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Ruby,</span><span class="nv"> </span><span class="s">Programmer's</span><span class="nv"> </span><span class="s">Best</span><span class="nv"> </span><span class="s">Friend"</span>
<span class="na">date</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2025-12-29</span><span class="nv"> </span><span class="s">08:00"</span>
<span class="na">permalink</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/blog/:title"</span>
<span class="na">assets</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/assets/posts/blog/2025/2025-12-29-ruby-programmers-best-friend"</span> <span class="c1"># 👈 direktori assets</span>
<span class="na">author</span><span class="pi">:</span> <span class="s2">"</span><span class="s">BanditHijo"</span>
<span class="na">category</span><span class="pi">:</span> <span class="s2">"</span><span class="s">blog"</span>
<span class="na">tags</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">ruby"</span><span class="pi">]</span>
<span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Ruby</span><span class="nv"> </span><span class="s">adalah</span><span class="nv"> </span><span class="s">bahasa</span><span class="nv"> </span><span class="s">pemrograman</span><span class="nv"> </span><span class="s">yang</span><span class="nv"> </span><span class="s">dirancang</span><span class="nv"> </span><span class="s">untuk</span><span class="nv"> </span><span class="s">kemudahan</span><span class="nv"> </span><span class="s">dan</span><span class="nv"> </span><span class="s">produktivitas."</span>
<span class="nn">---</span>
</code></pre></div></div>

<p>Kemudian pada artikel, saya dapat mengakses variabel <code class="language-plaintext highlighter-rouge">assets</code> dengan <code class="language-plaintext highlighter-rouge">{{ page.assets }}</code>.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Lorem ipsum dolor sit amet, consectetur adipiscing elit.

<span class="p">![</span><span class="nv">Gambar 1</span><span class="p">](</span><span class="sx">{{</span> page.assets }}/gambar-01.png)

<span class="p">![</span><span class="nv">Gambar 2</span><span class="p">](</span><span class="sx">{{</span> page.assets }}/gambar-02.png)

Lorem ipsum dolor sit amet, consectetur adipiscing elit, <span class="p">[</span><span class="nv">Download File 1</span><span class="p">](</span><span class="sx">{{</span> page.assets }}/file-01.pdf).
</code></pre></div></div>

<p>Dengan pendekatan ini, saya mendapatkan beberapa keuntungan, di antaranya:</p>
<ol>
  <li><strong>Terstruktur dengan baik</strong> <br />
  Setiap artikel memiliki direktori gambar sendiri yang terorganisir berdasarkan kategori dan tanggal postingan.</li>
  <li><strong>Mudah untuk dikelola</strong> <br />
  Jika saya perlu mengubah nama file gambar atau menghapus gambar tertentu, saya hanya perlu mengakses direktori gambar yang sesuai dengan artikel tersebut tanpa harus mencari di seluruh project.</li>
  <li><strong>Fleksibel untuk berbagai jenis file</strong> <br />
  Saya dapat menyimpan berbagai jenis file (gambar, PDF, dokumen, dll) di dalam direktori yang sama tanpa harus bergantung pada layanan pihak ketiga.</li>
</ol>

<h2 id="penutup">Penutup</h2>

<p>Tak terasa sudah sejak 2018 saya menulis di blog ini dengan berbagai format penulisan. Dari mulai menggunakan custom tag/notasi markdown di versi pertama, mencoba AsciiDoc di versi kedua, hingga akhirnya kembali ke Markdown dengan mengikuti CommonMark specification di versi ketiga ini.</p>

<p>Dengan perubahan ini, saya berharap dapat lebih fokus pada konten artikel tanpa terganggu oleh aspek teknis penulisan.</p>

<p>Terima kasih saya ucapkan untuk teman-teman yang telah mengikuti perjalanan blog ini hingga versi ketiga. Terima kasih karena telah memberikan testimonial dan feedback dalam beberapa kesempatan ketika berinteraksi dengan saya, baik secara langsung maupun tidak langsung melalui media sosial dan juga email. Saya senang jika blog ini dapat memberikan manfaat dan inspirasi buat teman-teman yang membacanya.</p>

<p>Mumpung masih awal tahun, saya mengajak teman-teman, “Ayo nulis blog juga yaa!”</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://github.com/bandithijo/bandithijo.github.io_v1">https://github.com/bandithijo/bandithijo.github.io_v1</a> <br />
  Tanggal diakses: 2026-01-16</p>
  </li>
  <li>
    <p><a href="https://github.com/bandithijo/bandithijo.github.io_v2">https://github.com/bandithijo/bandithijo.github.io_v2</a> <br />
  Tanggal diakses: 2026-01-16</p>
  </li>
  <li>
    <p><a href="https://github.com/bandithijo/bandithijo.github.io_v3">https://github.com/bandithijo/bandithijo.github.io_v3</a> <br />
  Tanggal diakses: 2026-01-16</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="blog" /><summary type="html"><![CDATA[Di pertengahan tahun 2025, saya memutuskan untuk membuat perubahan besar pada cara saya menulis artikel di blog ini. Setelah beberapa tahun menggunakan berbagai notasi syntax, saya merasa perlu untuk menyederhanakan proses penulisan agar tetap mempertahankan mood dalam menulis. Oleh karena itu, pada BanditHijo Versi 3, saya berfokus pada penggunaan CommonMark specification sebagai standar penulisan artikel.]]></summary></entry><entry><title type="html">Setup WordPress Multisite</title><link href="https://bandithijo.github.io/blog/setup-wordpress-multisite" rel="alternate" type="text/html" title="Setup WordPress Multisite" /><published>2026-01-13T06:28:00+08:00</published><updated>2026-01-13T06:28:00+08:00</updated><id>https://bandithijo.github.io/blog/setup-wordpress-multisite</id><content type="html" xml:base="https://bandithijo.github.io/blog/setup-wordpress-multisite"><![CDATA[<h2 id="prerequisites">Prerequisites</h2>

<p><code class="language-plaintext highlighter-rouge">wordpress 6.x</code></p>

<h2 id="pendahuluan">Pendahuluan</h2>

<p>WordPress Multisite adalah fitur bawaan WordPress yang memungkinkan Anda untuk membuat dan mengelola beberapa situs web dari satu instalasi WordPress. Fitur ini sangat berguna bagi mereka yang ingin mengelola jaringan situs web, seperti blog jaringan, situs klien, atau situs dengan subdomain atau subdirektori. Dalam catatan ini, saya akan mendokumentasikan langkah-langkah untuk mengatur WordPress Multisite.</p>

<h2 id="instalasi">Instalasi</h2>

<p>Ada beberapa tahapan proses instalasi WordPress Multisite.</p>

<h3 id="aktifkan-fitur-multisite">Aktifkan Fitur Multisite</h3>

<p>Edit file <code class="language-plaintext highlighter-rouge">wp-config.php</code> pada instalasi WordPress, dan cari bagian <code class="language-plaintext highlighter-rouge">/* That's all, stop editing! Happy publishing. */</code>. Tambakan kode berikut tepat sebelum baris tersebut.</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">!</span><span class="n">filename</span><span class="o">:</span> <span class="n">wp</span><span class="o">-</span><span class="n">config</span><span class="mf">.</span><span class="n">php</span>
<span class="cm">/* Multisite enable */</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'WP_ALLOW_MULTISITE'</span><span class="p">,</span> <span class="kc">true</span> <span class="p">);</span>

<span class="cm">/* That's all, stop editing! Happy publishing. */</span>
</code></pre></div></div>

<p>Setelah menambahkan kode tersebut, simpan perubahan pada file <code class="language-plaintext highlighter-rouge">wp-config.php</code>.</p>

<h3 id="akses-menu-network-setup">Akses Menu Network Setup</h3>

<p>Pada bagian WordPress Admin Dashboard, Anda akan melihat pada menu <strong>Tools</strong> terdapat submenu baru bernama <strong>Network Setup</strong>.</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_01.png" alt="Gambar 1" /></p>

<p>Gambar 1. Sebelum mengaktifkan fitur Multisite</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_02.png" alt="Gambar 2" /></p>

<p>Gambar 2. Setelah mengaktifkan fitur Multisite</p>

<p>Kemudian, coba refresh halaman admin dashboard WordPress Anda untuk memastikan bahwa fitur Multisite telah diaktifkan dengan benar.</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_03.png" alt="Gambar 3" /></p>

<p>Gambar 3. Halaman Network Setup</p>

<p>Karena saya membuat di localhost, maka yang direkomendasikan adalah menggunakan subdirektori.</p>

<p>Selain subdirektori sebenarnya ada pilihan subdomain, namun untuk menggunakan subdomain di localhost perlu konfigurasi tambahan pada server lokal. Jadi saya pakai subdirektori saja.</p>

<blockquote>
  <p>INFO</p>

  <p>Jika bukan di localhost, di halaman Network Setup, sebenarnya akan diminta untuk mengkonfigurasi jaringan multisite dengan pilihan apakah ingin menggunakan subdomain (site1.example.com) atau subdirektori (example.com/site1).</p>
</blockquote>

<p>Setelah menentukan <strong>Network Title</strong> dan <strong>Network Admin Email</strong>, klik tombol <strong>Install</strong> untuk melanjutkan proses instalasi.</p>

<h3 id="update-file-konfigurasi">Update File Konfigurasi</h3>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_04.png" alt="Gambar 4" /></p>

<p>Gambar 4. Konfigurasi tambahan setelah instalasi Multisite untuk dipasang di <code class="language-plaintext highlighter-rouge">wp-config.php</code> dan <code class="language-plaintext highlighter-rouge">.htaccess</code></p>

<p>Setelah mengkonfigurasi jaringan, WordPress akan memberikan beberapa kode yang perlu ditambahkan ke file <code class="language-plaintext highlighter-rouge">wp-config.php</code> dan <code class="language-plaintext highlighter-rouge">.htaccess</code>. Tambahkan kode tersebut sesuai petunjuk yang diberikan.</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">!</span><span class="n">filename</span><span class="o">:</span> <span class="n">wp</span><span class="o">-</span><span class="n">config</span><span class="mf">.</span><span class="n">php</span>
<span class="cm">/* Multisite enable */</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'WP_ALLOW_MULTISITE'</span><span class="p">,</span> <span class="kc">true</span> <span class="p">);</span>

<span class="cm">/* Multisite settings */</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'MULTISITE'</span><span class="p">,</span> <span class="kc">true</span> <span class="p">);</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'SUBDOMAIN_INSTALL'</span><span class="p">,</span> <span class="kc">false</span> <span class="p">);</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'DOMAIN_CURRENT_SITE'</span><span class="p">,</span> <span class="s1">'localhost'</span> <span class="p">);</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'PATH_CURRENT_SITE'</span><span class="p">,</span> <span class="s1">'/'</span> <span class="p">);</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'SITE_ID_CURRENT_SITE'</span><span class="p">,</span> <span class="mi">1</span> <span class="p">);</span>
<span class="nb">define</span><span class="p">(</span> <span class="s1">'BLOG_ID_CURRENT_SITE'</span><span class="p">,</span> <span class="mi">1</span> <span class="p">);</span>

<span class="cm">/* That's all, stop editing! Happy publishing. */</span>
</code></pre></div></div>

<div class="language-apache highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">!</span><span class="nc">filename</span>: .htaccess

<span class="c"># BEGIN WordPress</span>
<span class="c"># The directives (lines) between "BEGIN WordPress" and "END WordPress" are</span>
<span class="c"># dynamically generated, and should only be modified via WordPress filters.</span>
<span class="c"># Any changes to the directives between these markers will be overwritten.</span>
<span class="p">&lt;</span><span class="nl">IfModule</span><span class="sr"> mod_rewrite.c</span><span class="p">&gt;
</span><span class="nc">RewriteEngine</span> <span class="ss">On</span>
<span class="nc">RewriteRule</span> .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
<span class="nc">RewriteBase</span> /
<span class="nc">RewriteRule</span> ^index\.php$ - [L]

<span class="c"># add a trailing slash to /wp-admin</span>
<span class="nc">RewriteRule</span> ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

<span class="nc">RewriteCond</span> %{REQUEST_FILENAME} -f [OR]
<span class="nc">RewriteCond</span> %{REQUEST_FILENAME} -d
<span class="nc">RewriteRule</span> ^ - [L]
<span class="nc">RewriteRule</span> ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
<span class="nc">RewriteRule</span> ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
<span class="nc">RewriteRule</span> . index.php [L]
<span class="p">&lt;/</span><span class="nl">IfModule</span><span class="p">&gt;
</span>
<span class="c"># END WordPress</span>
</code></pre></div></div>

<p>Setelah ditambahkan, simpan perubahan pada kedua file tersebut.</p>

<p>Kemudian, tekan link “Log in” di pojok kiri bawah halaman tersebut untuk masuk kembali ke dashboard admin WordPress.</p>

<p>Setelah login kembali, akan terdapat menu <strong>My Sites</strong> di bagian atas dashboard admin WordPress, yang menunjukkan bahwa jaringan multisite telah berhasil dikonfigurasi.</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_05.png" alt="Gambar 5" /></p>

<p>Gambar 5. Menu My Sites di dashboard admin WordPress</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_06.png" alt="Gambar 6" /></p>

<p>Gambar 6. WordPress Network Admin Dashboard</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_07.png" alt="Gambar 7" /></p>

<p>Gambar 7. Daftar situs di WordPress Multisite</p>

<p><img src="/assets/posts/blog/2026/2026-01-13-setup-wordpress-multisite/gambar_08.png" alt="Gambar 8" /></p>

<p>Gambar 8. Plugin Install di WordPress Multisite. Bisa mengaktifkan untuk semua situs dengan “Network Activate”</p>

<p>Selesai!</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://developer.wordpress.org/advanced-administration/multisite/">WordPress: WordPress Multisite / Network</a> <br />
  Tanggal diakses: 2026-01-13</p>
  </li>
  <li>
    <p><a href="https://www.youtube.com/watch?v=NjYPQQq67MM">YouTube/WordPress: Setting up a WordPress multisite network</a> <br />
  Tanggal diakses: 2026-01-13</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="wordpress" /><summary type="html"><![CDATA[WordPress Multisite adalah fitur bawaan WordPress yang memungkinkan Anda untuk membuat dan mengelola beberapa situs web dari satu instalasi WordPress. Fitur ini sangat berguna bagi mereka yang ingin mengelola jaringan situs web, seperti blog jaringan, situs klien, atau situs dengan subdomain atau subdirektori. Dalam catatan ini, saya akan mendokumentasikan langkah-langkah untuk mengatur WordPress Multisite.]]></summary></entry><entry><title type="html">Setup Ruby LSP di Neovim</title><link href="https://bandithijo.github.io/blog/setup-ruby-lsp-di-neovim" rel="alternate" type="text/html" title="Setup Ruby LSP di Neovim" /><published>2026-01-10T00:24:00+08:00</published><updated>2026-01-10T00:24:00+08:00</updated><id>https://bandithijo.github.io/blog/setup-ruby-lsp-di-neovim</id><content type="html" xml:base="https://bandithijo.github.io/blog/setup-ruby-lsp-di-neovim"><![CDATA[<h2 id="prerequisites">Prerequisites</h2>

<p><code class="language-plaintext highlighter-rouge">neovim 0.10.x</code></p>

<h2 id="pendahuluan">Pendahuluan</h2>

<p>Language Server Protocol sangat membantu developer saat proses developement. Ruby LSP adalah LSP untuk Ruby yang terbilang masih cukup baru, yang mulai populer digunakan dikalangan Ruby programmer dan Rails developer. Catatan ini akan mendokumentasikan cara saya melakukan setup terhadap Ruby LSP di Neovim.</p>

<p><img src="/assets/posts/blog/2026/2026-01-10-setup-ruby-lsp-di-neovim/gambar_01.png" alt="Gambar 1" /></p>

<p>Gambar 1. Ruby LSP icon</p>

<blockquote>
  <p><em>The Ruby LSP is an implementation of the language server protocol for Ruby, used to improve rich features in editors. It is a part of a wider goal to provide a state-of-the-art experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.</em></p>
</blockquote>

<h2 id="instalasi">Instalasi</h2>

<h3 id="install-ruby-lsp-gem">Install ruby-lsp gem</h3>

<p>Install <code class="language-plaintext highlighter-rouge">ruby-lsp</code> gem menggunakan perintah berikut:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gem install ruby-lsp
</code></pre></div></div>

<h3 id="install-standard-gem">Install standard gem</h3>

<p>Karena saya menggunakan <code class="language-plaintext highlighter-rouge">standard</code> sebagai code formatter dan linter, maka saya juga menginstall <code class="language-plaintext highlighter-rouge">standard</code> gem.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gem install standard
</code></pre></div></div>

<h2 id="setup-ruby-lsp-di-neovim">Setup Ruby LSP di Neovim</h2>

<p>Saya menggunakan plugin <code class="language-plaintext highlighter-rouge">nvim-lspconfig</code> untuk mengatur LSP di Neovim.</p>

<p>Berikut adalah konfigurasi yang saya gunakan untuk mengaktifkan Ruby LSP di Neovim.</p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">!</span><span class="n">filename</span><span class="p">:</span> <span class="n">lua</span><span class="o">/</span><span class="n">lsp</span><span class="o">/</span><span class="n">init</span><span class="p">.</span><span class="n">lua</span>
<span class="kd">local</span> <span class="n">lspconfig</span> <span class="o">=</span> <span class="nb">require</span><span class="p">(</span><span class="s1">'lspconfig'</span><span class="p">)</span>
<span class="n">lspconfig</span><span class="p">.</span><span class="n">ruby_lsp</span><span class="p">.</span><span class="n">setup</span><span class="p">({</span>
  <span class="n">init_options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">formatter</span> <span class="o">=</span> <span class="s1">'standard'</span><span class="p">,</span>
    <span class="n">linters</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'standard'</span> <span class="p">},</span>
    <span class="n">addonSettings</span> <span class="o">=</span> <span class="p">{</span>
      <span class="n">rails</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
      <span class="n">rspec</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
    <span class="p">},</span>
  <span class="p">},</span>
<span class="p">})</span>
</code></pre></div></div>

<p>Just it! Ruby LSP sudah aktif di Neovim.</p>

<p>Hanya sesederhana itu saja instalasi dan konfigurasi Ruby LSP di Neovim. Selamat mencoba!</p>

<p>Dan untuk Rails, sudah otomatis terdeteksi jika berada di dalam project Rails.</p>

<blockquote>
  <p><em>Ruby LSP detects Rails projects and installs the Rails add-on for you.</em></p>
</blockquote>

<h2 id="pesan-penulis">Pesan Penulis</h2>

<p>Saya sangat merekomendasikan Ruby LSP ini untuk dicoba, terutama bagi Anda yang sering menggunakan Neovim sebagai editor utama. Fitur-fitur yang ditawarkan sangat membantu dalam meningkatkan produktivitas saat coding Ruby atau Rails.</p>

<p>Sejak Maret 2025, saya sudah mulai menggunakan Ruby LSP ini di Neovim, setelah sebelumnya sejak 2019 menggunakan Solargraph.</p>

<p><img src="/assets/posts/blog/2026/2026-01-10-setup-ruby-lsp-di-neovim/gambar_02.png" alt="Gambar 2" /></p>

<p>Gambar 2. Post terkait Ruby LSP di Threads saya.</p>

<p>Pengalaman saya sejauh ini sangat positif. Saya bahkan lupa kalau saya menggunakan Ruby LSP dan bukan lagi Solargraph.</p>

<h2 id="artikel-menarik-lainnya">Artikel Menarik Lainnya</h2>

<ol>
  <li><a href="https://railsatscale.com/2024-10-03-the-ruby-lsp-addon-system/">Andy Waite. An Introduction to the Ruby LSP Add-on System. 2024. Rails at Scale</a></li>
</ol>

<h2 id="referensi">Referensi</h2>

<ol>
  <li>
    <p><a href="https://shopify.github.io/ruby-lsp/">https://shopify.github.io/ruby-lsp/</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
  <li>
    <p><a href="https://shopify.github.io/ruby-lsp/editors#neovim">https://shopify.github.io/ruby-lsp/editors#neovim</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
  <li>
    <p><a href="https://shopify.github.io/ruby-lsp/rails-add-on.html">https://shopify.github.io/ruby-lsp/rails-add-on.html</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
  <li>
    <p><a href="https://github.com/Shopify/ruby-lsp">GitHub: Shopify/ruby-lsp</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
  <li>
    <p><a href="https://rubygems.org/gems/ruby-lsp">https://rubygems.org/gems/ruby-lsp</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
  <li>
    <p><a href="https://rubygems.org/gems/standard">https://rubygems.org/gems/standard</a> <br />
  Tanggal diakses: 2026-01-10</p>
  </li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="neovim" /><category term="ruby" /><summary type="html"><![CDATA[Language Server Protocol sangat membantu developer saat proses developement. Ruby LSP adalah LSP untuk Ruby yang terbilang masih cukup baru, yang mulai populer digunakan dikalangan Ruby programmer dan Rails developer. Catatan ini akan mendokumentasikan cara saya melakukan setup terhadap Ruby LSP di Neovim.]]></summary></entry><entry><title type="html">Rekam Aktivitas Coding dengan WakaTime</title><link href="https://bandithijo.github.io/blog/merekam-aktivitas-coding-dengan-wakatime" rel="alternate" type="text/html" title="Rekam Aktivitas Coding dengan WakaTime" /><published>2026-01-03T13:24:00+08:00</published><updated>2026-01-03T13:24:00+08:00</updated><id>https://bandithijo.github.io/blog/merekam-aktivitas-coding-dengan-wakatime</id><content type="html" xml:base="https://bandithijo.github.io/blog/merekam-aktivitas-coding-dengan-wakatime"><![CDATA[<h2 id="latar-belakang-masalah">Latar Belakang Masalah</h2>

<p>Tahun 2019 adalah tahun ketika saya memasuki dunia Software Development secara profesional untuk pertama kalinya.</p>

<p>Sebagai seorang Software Developer yang masih baru, saya memiliki banyak sekali tenaga dan <em>curiosity</em> untuk belajar hal-hal baru. Saya sering menghabiskan waktu berjam-jam di depan komputer untuk menulis kode, bereksperimen dengan teknologi baru, mengutak-atik sistem operasi Linux, dan menyelesaikan berbagai proyek percobaan.</p>

<p>Sampai suatu malam yang telah larut, saya melihat ada tetesan darah di atas meja kerja yang berasal dari hidung saya. Saya terkejut dan mulai menyadari bahwa mungkin saja saya telah menghabiskan terlalu banyak waktu di depan layar komputer tanpa istirahat yang cukup.</p>

<p>Kebetulan, pada saat itu di kantor tempat saya bekerja, sudah di-<em>encourage</em> untuk menggunakan WakaTime sebagai alat untuk melacak waktu (<em>time tracker</em>) yang dihabiskan di depan komputer. Saya coba melihat dashboard WakaTime. Ternyata, saya telah menghabiskan lebih dari 12 jam sehari di depan komputer selama beberapa hari berturut-turut.</p>

<h2 id="mengapa-menggunakan-wakatime">Mengapa Menggunakan WakaTime?</h2>

<p>WakaTime adalah alat pelacak waktu (<em>time tracker</em>) yang dirancang khusus untuk programmer. Dengan WakaTime, saya dapat melacak berapa lama waktu yang saya habiskan untuk menulis kode, serta mendapatkan wawasan tentang produktivitas saya. Beberapa alasan mengapa saya memilih WakaTime adalah:</p>

<ol>
  <li><strong>Mudah diintegrasikan dengan text editor</strong> <br />
  WakaTime dapat diintegrasikan dengan berbagai text editor populer seperti Neovim, Visual Studio Code, IntelliJ IDEA, dan lainnya. <br />
  <a href="https://wakatime.com/plugins">https://wakatime.com/plugins</a></li>
  <li><strong>Dashboard yang informatif</strong> <br />
  WakaTime menyediakan dashboard yang menampilkan statistik produktivitas penggunanya, termasuk waktu yang dihabiskan untuk berbagai proyek, bahasa pemrograman yang digunakan, dan tren produktivitas dari waktu ke waktu. <br />
  <a href="https://wakatime.com/dashboard">https://wakatime.com/dashboard</a></li>
  <li><strong>Alarm untuk istirahat</strong> <br />
  Dengan melihat statistik waktu yang dihabiskan, saya menjadi lebih sadar akan pentingnya istirahat. Saya mulai menetapkan batasan waktu kerja harian untuk menghindari kelelahan dan kondisi <em>burnout</em>.</li>
</ol>

<h2 id="shareables-status-wakatime">Shareables Status WakaTime</h2>

<p>WakaTime memungkinkan saya untuk membagikan statistik produktivitas saya dengan mudah melalui fitur <em>Shareables</em> <a href="https://wakatime.com/share">https://wakatime.com/share</a> (Login dengan akun WakaTime untuk membuat Shareables).</p>

<p>Berikut ini adalah beberapa contoh <em>Shareables</em> status WakaTime saya.</p>

<h3 id="total-time-coding-activity-since-sep-2019">Total time coding activity since Sep 2019</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <p><a href="https://wakatime.com/@1e45775c-2d2f-46db-9f24-f0724d2323ff"><img src="https://wakatime.com/badge/user/1e45775c-2d2f-46db-9f24-f0724d2323ff.svg" alt="wakatime" /></a></p>
</div>

<h3 id="coding-activity-last-7-days">Coding activity last 7 days</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <figure class="w-full md:w-3/4">
    <embed src="https://wakatime.com/share/@bandithijo/2edf2777-2d4c-4c09-be4c-327f7f04b318.svg" />
  </figure>
</div>

<h3 id="languages-over-the-last-7-days">Languages over the last 7 days</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <figure class="w-full md:w-3/4">
    <embed src="https://wakatime.com/share/@bandithijo/8147a29a-4fad-4df3-acbd-e4d8179918d1.svg" />
  </figure>
</div>

<h3 id="editors-over-the-last-7-days">Editors over the last 7 days</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <figure class="w-full md:w-3/4">
    <embed src="https://wakatime.com/share/@bandithijo/55242292-5c24-4b96-8cef-69a16a1a9179.svg" />
  </figure>
</div>

<h3 id="operating-systems-over-the-last-7-days">Operating Systems over the last 7 days</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <figure class="w-full md:w-3/4">
    <embed src="https://wakatime.com/share/@bandithijo/2fe44fdc-452b-4fa3-b177-2491af80e9fc.svg" />
  </figure>
</div>

<h3 id="categories-ver-the-last-7-days">Categories ver the last 7 days</h3>

<div class="bg-white p-10 flex flex-col mb-4 items-center">
  <figure class="w-full md:w-3/4">
    <embed src="https://wakatime.com/share/@bandithijo/7ec3fe1b-9c3e-4f9a-935b-47f4a9503fb9.svg" />
  </figure>
</div>

<h2 id="recap-code-stats-dari-wakatime-setiap-tahun">Recap Code Stats dari WakaTime setiap Tahun</h2>

<p>Di akhir tahun, WakaTime menyediakan ringkasan statistik tahunan yang menarik. Saya dapat melihat berapa banyak waktu yang saya habiskan untuk menulis kode setiap harinya, bahasa pemrograman yang paling sering saya gunakan, dan proyek-proyek yang paling banyak saya kerjakan.</p>

<p>Berikut ini adalah ringkasan statistik kode saya dari WakaTime setiap tahunnya sejak 2019 hingga 2025.</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_01.png" alt="Gambar 1" /></p>

<p>Gambar 1. Code Stats for 2019, <a href="https://wakatime.com/a-look-back-at-2019">https://wakatime.com/a-look-back-at-2019</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_02.png" alt="Gambar 2" /></p>

<p>Gambar 2. Code Stats for 2020, <a href="https://wakatime.com/a-look-back-at-2020">https://wakatime.com/a-look-back-at-2020</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_03.png" alt="Gambar 3" /></p>

<p>Gambar 3. Code Stats for 2021, <a href="https://wakatime.com/a-look-back-at-2021">https://wakatime.com/a-look-back-at-2021</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_04.png" alt="Gambar 4" /></p>

<p>Gambar 4. Code Stats for 2022, <a href="https://wakatime.com/a-look-back-at-2022">https://wakatime.com/a-look-back-at-2022</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_05.png" alt="Gambar 5" /></p>

<p>Gambar 5. Code Stats for 2023, <a href="https://wakatime.com/a-look-back-at-2023">https://wakatime.com/a-look-back-at-2023</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_06.png" alt="Gambar 6" /></p>

<p>Gambar 6. Code Stats for 2024, <a href="https://wakatime.com/a-look-back-at-2024">https://wakatime.com/a-look-back-at-2024</a> (Login to see your stats)</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_07.png" alt="Gambar 7" /></p>

<p>Gambar 7. Code Stats for 2025, <a href="https://wakatime.com/a-look-back-at-2025">https://wakatime.com/a-look-back-at-2025</a> (Login to see your stats)</p>

<h2 id="strava-but-for-programmers">Strava but for Programmers</h2>

<p>Kalau di dunia olahraga ada Strava yang digunakan untuk melacak aktivitas bersepeda atau lari, maka WakaTime bisa dianggap sebagai Strava-nya para programmer. Dengan WakaTime, saya bisa melihat “rute” coding saya, “kecepatan” menulis kode, dan “jarak” yang telah saya tempuh dalam dunia pemrograman.</p>

<p><img src="https://bandithijo.github.io/assets/posts/blog/2026/2026-01-03-merekam-aktivitas-coding-dengan-wakatime/gambar_08.png" alt="Gambar 8" /></p>

<p>Gambar 8. Saya memodifikasi Gambar 7 dengan memberikan background foto laptop saya dengan menggunakan GIMP (GNI Image Manipulation Program)</p>

<h2 id="faq">FAQ</h2>

<p>Untuk membantu memahami WakaTime lebih jauh, bisa dibaca di halaman FAQ resmi WakaTime di <a href="https://wakatime.com/faq">https://wakatime.com/faq</a>.</p>

<p>Halaman ini meliputi berbagai pertanyaan umum seperti:</p>
<ol>
  <li>Bagaimana cara kerja WakaTime?</li>
  <li>Apakah WakaTime aman digunakan?</li>
  <li>Bagaimana project terdeteksi oleh WakaTime?</li>
  <li>Bagaimana cara men-disable WakaTime di folder tertentu?</li>
  <li>dll.</li>
</ol>

<h2 id="pesan-penulis">Pesan Penulis</h2>

<p>Melacak waktu yang dihabiskan untuk coding dengan WakaTime telah membantu saya menjadi lebih sadar akan produktivitas dan pentingnya menjaga keseimbangan antara kerja dan istirahat.</p>

<p>Saya merekomendasikan WakaTime kepada sesama teman programmer untuk membantu mereka memahami kebiasaan kerja mereka dan meningkatkan produktivitas secara keseluruhan.</p>

<p>Terima kasih telah membaca cerita saya tentang pengalaman menggunakan WakaTime. Semoga bermanfaat!</p>

<h2 id="referensi">Referensi</h2>

<ol>
  <li><a href="https://wakatime.com/">https://wakatime.com/</a> <br />
  Tanggal diakses: 2026-01-04</li>
</ol>]]></content><author><name>BanditHijo</name></author><category term="blog" /><category term="wakatime" /><summary type="html"><![CDATA[WakaTime adalah time tracker yang membantu programmer untuk merekam waktu yang dihabiskan ketika berproduktifitas, seperti berapa lama waktu yang dihabiskan ketika sedang ngoding. Berikut ini adalah cerita yang bisa saya bagikan tentang pengalaman menggunakan WakaTime sejak tahun 2019.]]></summary></entry></feed>