URL Decode JavaScript — decodeURIComponent()
Gunakan URL Decode Online gratis langsung di browser Anda — tidak perlu instalasi.
Coba URL Decode Online Online →String yang disandikan dengan persen muncul terus-menerus dalam kode JavaScript — sebuah kueri pencarian tiba sebagai q=standing+desk%26price%3A200, redirect OAuth sebagai next=https%3A%2F%2Fdashboard.internal%2F, jalur penyimpanan sebagai reports%2F2025%2Fq1.pdf. Cara mendekode URL di JavaScript bergantung pada memilih fungsi bawaan yang tepat dari tiga pilihan: decodeURIComponent(), decodeURI(), dan URLSearchParams — dan pilihan di antara ketiganya adalah akar penyebab sebagian besar korupsi data diam-diam yang saya temukan di basis kode produksi, khususnya edge case +-sebagai-spasi dan double-decoding. Untuk dekode cepat tanpa menulis kode, Dekoder URL ToolDeck menanganinya secara instan di browser. Tutorial dekode URL JavaScript ini mencakup ketiga fungsi secara mendalam (ES2015+ / Node.js 10+): kapan menggunakan masing-masing, bagaimana perbedaannya untuk spasi dan karakter terreservasi, dekoding dari file dan permintaan HTTP, penanganan error yang aman, dan empat kesalahan yang menyebabkan bug produksi paling halus.
- ✓decodeURIComponent() mendekode semua urutan percent-encoded — ini adalah pilihan tepat untuk nilai parameter query individual dan segmen jalur
- ✓decodeURI() mempertahankan karakter URI struktural seperti / ? & = # : — gunakan hanya saat mendekode string URL lengkap, tidak pernah untuk nilai individual
- ✓URLSearchParams.get() mengembalikan nilai yang sudah didekode — memanggil decodeURIComponent() di atasnya menyebabkan double-decoding
- ✓decodeURIComponent() TIDAK mendekode + sebagai spasi — untuk data form-encoded (application/x-www-form-urlencoded), gunakan URLSearchParams atau ganti + sebelum mendekode
- ✓Selalu bungkus decodeURIComponent() dalam try/catch jika input berasal dari data pengguna — karakter % tunggal atau urutan tidak lengkap melempar URIError
Apa itu URL Decoding?
Percent-encoding (secara resmi didefinisikan dalam RFC 3986) menggantikan karakter yang tidak aman atau signifikan secara struktural dalam URL dengan tanda % diikuti dua digit heksadesimal — nilai byte UTF-8 karakter tersebut. URL decoding membalikkan transformasi ini: setiap urutan %XX dikonversi kembali ke byte aslinya, dan urutan byte yang dihasilkan diinterpretasikan sebagai teks UTF-8. Spasi didekode dari %20, garis miring dari %2F, karakter non-ASCII ü dari %C3%BC (representasi dua-byte UTF-8-nya).
Karakter yang tidak pernah disandikan — disebut karakter tak-terreservasi — adalah huruf A–Z dan a–z, angka 0–9, dan - _ . ~. Semua yang lain memiliki peran struktural dalam URL (seperti / yang memisahkan segmen jalur atau & yang memisahkan parameter query) atau harus disandikan saat digunakan sebagai data. Hasil praktisnya: filter pencarian seperti status=active&tier=premium yang disandikan sebagai nilai parameter query tunggal tiba dengan tampilan yang sama sekali berbeda dari aslinya.
// Disandikan dengan persen — seperti diterima dalam permintaan HTTP atau payload webhook "q=price%3A%5B200+TO+800%5D%20AND%20brand%3ANorthwood%20%26%20status%3Ain-stock"
// Setelah URL decoding — filter kueri Elasticsearch asli "q=price:[200 TO 800] AND brand:Northwood & status:in-stock"
decodeURIComponent() — Fungsi Standar untuk Mendekode Nilai
decodeURIComponent() adalah tulang punggung URL decoding di JavaScript. Fungsi ini mendekode setiap urutan %XX dalam string input — termasuk karakter yang memiliki arti struktural dalam URL, seperti %2F (garis miring), %3F (tanda tanya), %26 (ampersand), dan %3D (tanda sama dengan). Ini menjadikannya pilihan yang tepat untuk mendekode nilai parameter query individual dan segmen jalur, tetapi pilihan yang salah untuk mendekode URL lengkap — di mana karakter struktural tersebut harus tetap disandikan. Ini adalah fungsi global: tidak diperlukan impor di lingkungan JavaScript mana pun.
Contoh minimal yang berfungsi
// Mendekode nilai parameter query individual — kasus umum
const city = decodeURIComponent('Jakarta%20Selatan') // 'Jakarta Selatan'
const district = decodeURIComponent('Kebayoran%20Baru') // 'Kebayoran Baru'
const category = decodeURIComponent('perabot%20kantor') // 'perabot kantor'
const filter = decodeURIComponent('price%3A%5B200+TO+800%5D') // 'price:[200+TO+800]'
// Catatan: + TIDAK didekode sebagai spasi — lihat bagian + di tabel perbandingan
console.log(city) // Jakarta Selatan
console.log(district) // Kebayoran Baru
console.log(category) // perabot kantor
console.log(filter) // price:[200+TO+800]Mendekode URL redirect yang diekstrak dari parameter query
// URL redirect disandikan dengan persen saat disematkan sebagai nilai parameter
// sisi penerima: ekstrak lalu gunakan — tidak perlu dekoding manual dengan URLSearchParams
const incomingUrl = 'https://auth.company.com/callback' +
'?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform' +
'&session_id=sid_7x9p2k'
const url = new URL(incomingUrl)
const rawNext = url.searchParams.get('next') // Auto-decoded oleh URLSearchParams
const sessionId = url.searchParams.get('session_id') // 'sid_7x9p2k'
// rawNext sudah didekode: 'https://dashboard.internal/reports?view=weekly&team=platform'
// JANGAN panggil decodeURIComponent(rawNext) lagi — itu akan menyebabkan double-decoding
const nextUrl = new URL(rawNext!)
console.log(nextUrl.hostname) // dashboard.internal
console.log(nextUrl.searchParams.get('view')) // weekly
console.log(nextUrl.searchParams.get('team')) // platformMendekode segmen jalur non-ASCII dan Unicode
// REST API dengan segmen jalur yang diinternasionalisasi
// Setiap byte UTF-8 dari karakter asli disandikan secara terpisah
const encodedSegments = [
'%E6%9D%B1%E4%BA%AC', // 東京 (Tokyo) — 3 byte per karakter
'M%C3%BCnchen', // München — ü disandikan sebagai 2 byte
'caf%C3%A9', // café — é sebagai NFC precomposed
'Bandung%20Raya', // Bandung Raya
]
encodedSegments.forEach(seg => {
console.log(decodeURIComponent(seg))
})
// 東京
// München
// café
// Bandung Raya
// Mengekstrak kunci file dari URL API penyimpanan
// Kunci objek berisi / sehingga disandikan sebagai %2F di dalam segmen jalur
const storageUrl = 'https://storage.api.example.com/v1/objects/reports%2F2025%2Fq1-financials.pdf'
const rawKey = new URL(storageUrl).pathname.replace('/v1/objects/', '')
// .pathname mendekode enkoding level URL tetapi %2F (sebagai %252F di level URL) tetap
// Gunakan decodeURIComponent untuk langkah terakhir:
const fileKey = decodeURIComponent(rawKey) // 'reports/2025/q1-financials.pdf'
console.log(fileKey)decodeURIComponent() melempar URIError ketika input mengandung % yang tidak diikuti dua digit heksadesimal yang valid — misalnya % tunggal di akhir string atau urutan seperti %GH. Selalu bungkus dalam try/catch saat mendekode input yang diberikan pengguna. Pola dekoding yang aman dibahas di bagian Penanganan Error di bawah.Fungsi URL Decoding JavaScript — Referensi Karakter
Ketiga fungsi dekoding bawaan berbeda dalam urutan yang tepat mana yang mereka dekode. Tabel ini menunjukkan perilaku untuk karakter yang paling penting dalam praktik:
| Disandikan | Karakter | decodeURIComponent() | decodeURI() | URLSearchParams |
|---|---|---|---|---|
| %20 | spasi | spasi ✅ | spasi ✅ | spasi ✅ |
| + | plus (form) | + (dipertahankan) | + (dipertahankan) | spasi ✅ |
| %2B | + literal | + ✅ | + ✅ | + ✅ |
| %26 | & | & ✅ | & (dipertahankan) ❌ | & ✅ |
| %3D | = | = ✅ | = (dipertahankan) ❌ | = ✅ |
| %3F | ? | ? ✅ | ? (dipertahankan) ❌ | ? ✅ |
| %23 | # | # ✅ | # (dipertahankan) ❌ | # ✅ |
| %2F | / | / ✅ | / (dipertahankan) ❌ | / ✅ |
| %3A | : | : ✅ | : (dipertahankan) ❌ | : ✅ |
| %40 | @ | @ ✅ | @ (dipertahankan) ❌ | @ ✅ |
| %25 | % | % ✅ | % ✅ | % ✅ |
| %C3%BC | ü | ü ✅ | ü ✅ | ü ✅ |
Dua baris kritis adalah + dan karakter struktural (%26, %3D, %3F). URLSearchParams mendekode + sebagai spasi karena mengikuti spesifikasi application/x-www-form-urlencoded — benar untuk pengiriman formulir HTML, tetapi berbeda dari apa yang dilakukan decodeURIComponent() dengan karakter yang sama. Dan decodeURI() secara diam-diam melewati %26, %3D, dan %3F — yang terlihat benar sampai sebuah nilai benar-benar mengandung ampersand atau tanda sama yang disandikan.
decodeURI() — Mendekode URL Lengkap Tanpa Merusak Strukturnya
decodeURI() adalah pasangan dari encodeURI(). Fungsi ini mendekode string URL lengkap sambil mempertahankan karakter yang memiliki arti struktural dalam URI: ; , / ? : @ & = + $ #. Karakter-karakter ini dibiarkan dalam bentuk percent-encoded (atau sebagai karakter literal jika muncul tidak tersandikan dalam input). Ini membuat decodeURI() aman untuk membersihkan URL lengkap yang mungkin mengandung karakter non-ASCII dalam jalur atau nama host, tanpa secara tidak sengaja menghancurkan struktur query string.
Membersihkan URL dengan segmen jalur non-ASCII
// URL CDN dengan segmen jalur yang diinternasionalisasi dan query string terstruktur // decodeURI() mendekode jalur non-ASCII tetapi mempertahankan ? & = tetap utuh const encodedUrl = 'https://cdn.example.com/assets/%E6%9D%B1%E4%BA%AC%2F2025%2Fq1-report.pdf' + '?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600' const readable = decodeURI(encodedUrl) console.log(readable) // https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600 // ↑ Non-ASCII didekode; ? & = dipertahankan — URL tetap valid secara struktural // decodeURIComponent akan merusak URL — : / ? & = semua didekode sekaligus const broken = decodeURIComponent(encodedUrl) // 'https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600' // Terlihat sama di sini, tetapi URL seperti 'https%3A%2F%2F...' akan hancur
URL daripada decodeURI() ketika Anda juga perlu mengakses komponen URL individual. new URL(str) menormalkan input, memvalidasi strukturnya, dan mengekspos .pathname, .searchParams, dan .hostname sebagai properti yang sudah didekode. Cadangkan decodeURI() untuk kasus di mana Anda hanya membutuhkan hasil string dan tidak dapat menggunakan konstruktor URL (misalnya, di lingkungan Node.js 6 yang sangat lama tanpa kelas URL global).URLSearchParams — Dekoding Otomatis untuk Query String
URLSearchParams adalah cara idiomatis untuk mengurai query string di JavaScript modern. Setiap nilai yang dikembalikan oleh .get(), .getAll(), atau iterasi didekode secara otomatis — termasuk + sebagai spasi untuk data form-encoded. Tersedia secara global di semua browser modern dan Node.js 10+, tidak memerlukan impor, dan menangani edge case yang penguraian string manual sering keliru.
Mengurai query string yang masuk
// Mengurai query string callback webhook atau redirect OAuth
const rawSearch =
'?event_id=evt_9c2f4a1b' +
'&product_name=Standing+Desk+Pro' +
'&filter=price%3A%5B200+TO+800%5D' +
'&tag=ergonomic&tag=adjustable' +
'&redirect=https%3A%2F%2Fdashboard.internal%2Forders%3Fview%3Dpending'
const params = new URLSearchParams(rawSearch)
console.log(params.get('event_id')) // 'evt_9c2f4a1b'
console.log(params.get('product_name')) // 'Standing Desk Pro' ← + didekode sebagai spasi
console.log(params.get('filter')) // 'price:[200+TO+800]' ← %3A dan %5B didekode
console.log(params.getAll('tag')) // ['ergonomic', 'adjustable']
console.log(params.get('redirect')) // 'https://dashboard.internal/orders?view=pending'
// Mengiterasi semua parameter
for (const [key, value] of params) {
console.log(`${key}: ${value}`)
}
// event_id: evt_9c2f4a1b
// product_name: Standing Desk Pro
// filter: price:[200+TO+800]
// tag: ergonomic
// tag: adjustable
// redirect: https://dashboard.internal/orders?view=pendingMengurai parameter query dari URL browser saat ini
// URL saat ini: https://app.example.com/search
// ?q=standing+desk
// &category=perabot+kantor
// &sort=price_asc
// &page=2
interface SearchFilters {
query: string | null
category: string | null
sort: string
page: number
}
function getSearchFilters(): SearchFilters {
const params = new URLSearchParams(window.location.search)
return {
query: params.get('q'), // 'standing desk'
category: params.get('category'), // 'perabot kantor'
sort: params.get('sort') ?? 'relevance',
page: Number(params.get('page') ?? '1'),
}
}
const filters = getSearchFilters()
console.log(filters.query) // standing desk (+ didekode otomatis)
console.log(filters.category) // perabot kantorMendekode Data URL-Encoded dari File dan Respons API
Dua skenario muncul terus-menerus dalam proyek nyata: memproses file di disk yang mengandung data percent-encoded (log akses, ekspor data, file tangkapan webhook), dan mengurai URL dari permintaan HTTP yang masuk di server Node.js. Keduanya mengikuti prinsip yang sama — gunakan konstruktor URL atau URLSearchParams daripada pemisahan string manual — tetapi detailnya berbeda.
Membaca dan mendekode rekaman URL-encoded dari file
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// File: orders-export.txt — satu rekaman URL-encoded per baris
// customer_name=Budi+Santoso&order_id=ord_9c2f4a&product=Standing+Desk+Pro&total=2249000%20IDR
// customer_name=Siti+Rahayu&order_id=ord_7b3a1c&product=Ergonomic+Chair&total=1350000%20IDR
// customer_name=Agus+Wijaya&order_id=ord_2e8d5f&product=Monitor+Arm&total=599000%20IDR
interface Order {
customerName: string | null
orderId: string | null
product: string | null
total: string | null
}
async function parseOrdersFile(filePath: string): Promise<Order[]> {
const fileStream = createReadStream(filePath, { encoding: 'utf-8' })
const rl = createInterface({ input: fileStream, crlfDelay: Infinity })
const orders: Order[] = []
for await (const line of rl) {
if (!line.trim()) continue
// URLSearchParams mendekode + sebagai spasi dan urutan %XX secara otomatis
const params = new URLSearchParams(line)
orders.push({
customerName: params.get('customer_name'), // 'Budi Santoso'
orderId: params.get('order_id'), // 'ord_9c2f4a'
product: params.get('product'), // 'Standing Desk Pro'
total: params.get('total'), // '2249000 IDR'
})
}
return orders
}
const orders = await parseOrdersFile('./orders-export.txt')
console.log(orders[0])
// { customerName: 'Budi Santoso', orderId: 'ord_9c2f4a', product: 'Standing Desk Pro', total: '2249000 IDR' }Mengurai log akses Nginx untuk mendekode kueri pencarian
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// Baris log akses Nginx terlihat seperti:
// 192.168.1.42 - - [11/Mar/2026:10:23:01 +0000] "GET /api/search?q=standing%20desk%26brand%3ANorthwood&sort=price_asc HTTP/1.1" 200 1842
async function extractSearchQueries(logFile: string): Promise<string[]> {
const rl = createInterface({ input: createReadStream(logFile), crlfDelay: Infinity })
const queries: string[] = []
for await (const line of rl) {
// Ekstrak jalur permintaan dari baris log
const match = line.match(/"GET ([^ ]+) HTTP/)
if (!match) continue
try {
const requestUrl = new URL(match[1], 'http://localhost')
const query = requestUrl.searchParams.get('q')
if (query) queries.push(query) // URLSearchParams mendekode secara otomatis
} catch {
// Lewati baris yang tidak valid — log akses mungkin mengandung entri terpotong
}
}
return queries
}
const queries = await extractSearchQueries('/var/log/nginx/access.log')
console.log(queries)
// ['standing desk&brand:Northwood', 'ergonomic chair', 'monitor arm 27 inch']Mengurai parameter query di server HTTP Node.js
import http from 'http'
// URL masuk: /api/products?q=standing+desk&warehouse=jakarta%2Dselatan&minStock=10&cursor=eyJpZCI6MTIzfQ%3D%3D
const server = http.createServer((req, res) => {
// Buat URL lengkap — argumen kedua adalah base yang diperlukan oleh konstruktor URL
const requestUrl = new URL(req.url!, 'http://localhost')
const searchQuery = requestUrl.searchParams.get('q') // 'standing desk'
const warehouseId = requestUrl.searchParams.get('warehouse') // 'jakarta-selatan'
const minStock = Number(requestUrl.searchParams.get('minStock') ?? '0')
const cursor = requestUrl.searchParams.get('cursor') // 'eyJpZCI6MTIzfQ=='
if (!warehouseId) {
res.writeHead(400, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ error: 'parameter warehouse wajib diisi' }))
return
}
const cursorData = cursor ? JSON.parse(Buffer.from(cursor, 'base64').toString()) : null
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ searchQuery, warehouseId, minStock, cursorData }))
})
server.listen(3000)Ketika Anda perlu memeriksa URL yang disandikan selama pengembangan — untuk memahami apa yang dikirim webhook sebelum menulis kode penguraian — tempel langsung ke Dekoder URL ToolDeck untuk melihat bentuk yang didekode secara instan tanpa menjalankan skrip.
Dekoding URL Baris Perintah
Untuk skrip shell, pipeline CI, atau inspeksi cepat satu kali dari string yang disandikan, beberapa pendekatan bekerja tanpa menulis skrip penuh. One-liner Node.js bersifat lintas-platform; di macOS dan Linux, python3 juga selalu tersedia.
# ── One-liner Node.js ──────────────────────────────────────────────────
# Mendekode satu nilai percent-encoded
node -e "console.log(decodeURIComponent(process.argv[1]))" "Jakarta%20Selatan%20%26%20Surabaya"
# Jakarta Selatan & Surabaya
# Mengurai query string dan mencetak setiap pasangan key=value (yang sudah didekode)
node -e "
const params = new URLSearchParams(process.argv[1])
for (const [k, v] of params) console.log(`${k} = ${v}`)
" "q=standing+desk&warehouse=bandung%2Dutara&minStock=10"
# q = standing desk
# warehouse = bandung-utara
# minStock = 10
# Mendekode dan mencetak indah body JSON yang disandikan URL (umum dalam debugging webhook)
node -e "
const raw = decodeURIComponent(process.argv[1])
console.log(JSON.stringify(JSON.parse(raw), null, 2))
" '%7B%22event%22%3A%22purchase%22%2C%22amount%22%3A149.99%2C%22currency%22%3A%22IDR%22%7D'
# {
# "event": "purchase",
# "amount": 149.99,
# "currency": "IDR"
# }
# ── One-liner Python (tersedia di sebagian besar sistem macOS/Linux) ─────────────
# unquote_plus juga mendekode + sebagai spasi — benar untuk data form-encoded
python3 -c "
from urllib.parse import unquote_plus
import sys
print(unquote_plus(sys.argv[1]))
" "Standing+Desk+%26+Ergonomic+Chair"
# Standing Desk & Ergonomic Chair
# ── curl — log dan dekode URL redirect dari header respons ──────────
curl -sI "https://api.example.com/short/abc123" | grep -i location | node -e "
const line = require('fs').readFileSync('/dev/stdin', 'utf8')
const url = line.replace(/^location:s*/i, '').trim()
console.log(decodeURIComponent(url))
"Dekoding Aman dengan decode-uri-component
Fungsi bawaan decodeURIComponent() melempar URIError pada urutan yang tidak valid mana pun — termasuk % di akhir tanpa dua digit hex berikutnya. Dalam kode produksi yang memproses URL yang diberikan pengguna atau pihak ketiga — log kueri pencarian, URL klik-tayang dari kampanye email, data web yang di-scrape — urutan yang tidak valid cukup umum untuk menyebabkan crash. Paket decode-uri-component (~30 juta unduhan npm mingguan) menangani urutan yang tidak valid secara anggun dengan mengembalikan urutan asli tanpa berubah alih-alih melempar.
npm install decode-uri-component # atau pnpm add decode-uri-component
import decodeUriComponent from 'decode-uri-component'
// Fungsi native melempar pada input yang tidak valid — dapat merusak request handler
try {
decodeURIComponent('product%name%') // ❌ URIError: URI malformed
} catch (e) {
console.error('Native melempar:', (e as Error).message)
}
// decode-uri-component mengembalikan urutan mentah alih-alih melempar
console.log(decodeUriComponent('product%name%'))
// product%name% ← urutan yang tidak valid dibiarkan apa adanya, tidak melempar
// Urutan yang valid didekode dengan benar
console.log(decodeUriComponent('Bandung%20Raya%20%26%20Jakarta'))
// Bandung Raya & Jakarta
// Campuran: urutan yang valid didekode, yang tidak valid dipertahankan
console.log(decodeUriComponent('Jakarta%20Pusat%20%ZZ%20HQ'))
// Jakarta Pusat %ZZ HQ ← %ZZ bukan hex yang valid — dibiarkan apa adanya
// Penggunaan praktis dalam pipeline pemrosesan log
const rawSearchQueries = [
'standing%20desk%20ergonomic',
'price%3A%5B200+TO+800%5D',
'50%25+off', // ← akan melempar dengan decodeURIComponent (% tunggal)
'wireless%20keyboard%20%26%20mouse',
]
const decoded = rawSearchQueries.map(q => decodeUriComponent(q))
console.log(decoded)
// ['standing desk ergonomic', 'price:[200+TO+800]', '50%25+off', 'wireless keyboard & mouse']Gunakan decodeURIComponent() dengan try/catch untuk kode aplikasi di mana Anda ingin segera mengetahui apakah input yang tidak diharapkan tiba. Gunakan decode-uri-component dalam pipeline data, prosesor log, dan handler webhook di mana Anda ingin terus memproses meskipun beberapa input mengandung enkoding yang tidak valid.
Menangani String Percent-Encoded yang Tidak Valid
Karakter % tunggal, urutan tidak lengkap seperti %A, atau pasangan tidak valid seperti %GH semua menyebabkan decodeURIComponent() melempar URIError: URI malformed. Input apa pun yang dikendalikan pengguna — kueri pencarian, fragmen URL, kolom formulir yang mengandung URL, parameter dari tautan kampanye email — dapat mengandung urutan ini. Pembungkus yang aman sangat penting untuk kode yang menghadap ke luar.
Pembungkus dekode aman untuk skenario umum
// ── 1. Dekode aman dasar — mengembalikan string asli jika error ─────────
function safeDecode(encoded: string): string {
try {
return decodeURIComponent(encoded)
} catch {
return encoded // kembalikan input mentah jika dekoding gagal — jangan crash
}
}
// ── 2. Dekode aman + tangani + sebagai spasi (untuk nilai form-encoded) ─────────
function safeFormDecode(formEncoded: string): string {
try {
return decodeURIComponent(formEncoded.replace(/+/g, ' '))
} catch {
return formEncoded.replace(/+/g, ' ') // setidaknya ganti + meskipun sisanya gagal
}
}
// ── 3. Parser query string aman yang menghasilkan objek biasa ──────────────────────
function parseQueryString(queryString: string): Record<string, string> {
const result: Record<string, string> = {}
try {
const params = new URLSearchParams(queryString)
for (const [key, value] of params) {
result[key] = value // URLSearchParams menangani semua dekoding secara internal
}
} catch {
// Diam-diam kembalikan kosong pada input yang sepenuhnya tidak valid
}
return result
}
// Penggunaan
console.log(safeDecode('Jakarta%20Selatan')) // Jakarta Selatan
console.log(safeDecode('search%20for%2050%25+off')) // search for 50% off (% tunggal)
// → sebenarnya baik-baik saja; % di sini adalah %25
console.log(safeDecode('malformed%string%')) // malformed%string% (tidak melempar)
console.log(safeFormDecode('standing+desk+pro')) // standing desk pro
console.log(parseQueryString('q=hello+world&tag=node%20js&page=2'))
// { q: 'hello world', tag: 'node js', page: '2' }Kesalahan Umum
Saya telah melihat empat pola ini menyebabkan korupsi data diam-diam atau crash yang tidak terduga dalam produksi — biasanya hanya ketika sebuah nilai kebetulan mengandung karakter khusus, yang berarti mereka lolos dari unit test dan muncul dengan data pengguna nyata.
Kesalahan 1 — Double-decoding nilai URLSearchParams
Masalah: URLSearchParams.get() mengembalikan string yang sudah didekode. Memanggil decodeURIComponent() di atasnya mendekode ganda nilai — mengubah setiap sisa % dalam output yang didekode menjadi %25, yang merusak data. Perbaikan: gunakan nilai dari URLSearchParams.get() langsung — tidak diperlukan dekoding tambahan.
// ❌ URLSearchParams sudah mendekode — mendekode lagi merusak nilai dengan %
const qs = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rawRate = qs.get('rate') // '50%' ← sudah didekode
const wrongRate = decodeURIComponent(rawRate)
// '50%25' ← % didekode menjadi %25 pada pass kedua — sekarang salah lagi// ✅ Gunakan URLSearchParams.get() langsung — dekoding otomatis
const qs = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rate = qs.get('rate') // '50%' ← benar, tidak perlu langkah tambahan
const redirect = qs.get('redirect') // 'https://dashboard.internal/'
const parsed = new URL(redirect!) // Aman untuk dikonstruksi — sudah didekode
console.log(parsed.hostname) // dashboard.internalKesalahan 2 — Tidak mendekode + sebagai spasi untuk data form-encoded
Masalah: Pengiriman formulir dan beberapa library OAuth menyandikan spasi sebagai + (application/x-www-form-urlencoded). Memanggil decodeURIComponent() pada data ini membiarkan + sebagai tanda plus literal. Perbaikan: gunakan URLSearchParams untuk mengurai data form-encoded, atau ganti + dengan spasi sebelum memanggil decodeURIComponent().
// ❌ decodeURIComponent memperlakukan + sebagai tanda plus literal, bukan spasi
// Endpoint token OAuth mengirim: grant_type=authorization_code&code=SplxlOBeZQQYb...
// &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
// Beberapa implementasi OAuth lama juga menggunakan + untuk spasi dalam kode
const formBody = 'customer_name=Budi+Santoso&product=Standing+Desk+Pro&qty=2'
const [, rawVal] = formBody.split('&')[0].split('=')
const name = decodeURIComponent(rawVal)
console.log(name) // 'Budi+Santoso' ← + tidak dikonversi ke spasi// ✅ Gunakan URLSearchParams — mengikuti spesifikasi application/x-www-form-urlencoded
const formBody = 'customer_name=Budi+Santoso&product=Standing+Desk+Pro&qty=2'
const params = new URLSearchParams(formBody)
console.log(params.get('customer_name')) // 'Budi Santoso' ← + didekode dengan benar sebagai spasi
console.log(params.get('product')) // 'Standing Desk Pro'Kesalahan 3 — Menggunakan decodeURI() untuk nilai parameter query individual
Masalah: decodeURI() tidak mendekode %26 (&), %3D (=), atau %3F (?) — karakter yang umum disandikan di dalam nilai parameter. Menggunakannya untuk mendekode satu nilai membiarkan urutan tersebut tetap utuh, secara diam-diam menghasilkan output yang salah. Perbaikan: gunakan decodeURIComponent() untuk nilai individual; cadangkan decodeURI() untuk string URL lengkap.
// ❌ decodeURI tidak mendekode & dan = — nilai tetap rusak const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' const filter = decodeURI(encodedFilter) console.log(filter) // 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' ← %3D dan %26 tidak didekode
// ✅ decodeURIComponent mendekode semua urutan termasuk & dan = const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west' const filter = decodeURIComponent(encodedFilter) console.log(filter) // 'status=active&tier=premium®ion=eu-west' ← didekode dengan benar
Kesalahan 4 — Tidak membungkus decodeURIComponent dalam try/catch untuk input pengguna
Masalah: Karakter %tunggal yang tidak diikuti dua digit hex — umum dalam kueri pencarian yang diketik pengguna (“50% off”, “100% cotton”) — menyebabkan decodeURIComponent() melempar URIError: URI malformed, yang merusak request handler jika tidak tertangkap. Perbaikan: selalu bungkus dalam try/catch ketika input berasal dari data pengguna, fragmen URL, atau sistem eksternal.
// ❌ Pengguna mengetik '100% cotton' di kotak pencarian — % tunggal merusak server
// GET /api/search?q=100%25+cotton ← Kasus spesifik ini aman (%25 = %)
// GET /api/search?q=100%+cotton ← Ini crash (% tidak diikuti 2 digit hex)
app.get('/api/search', (req, res) => {
const query = decodeURIComponent(req.query.q as string)
// ↑ melempar URIError: URI malformed untuk '100% cotton' → error 500 yang tidak tertangani
})// ✅ Bungkus dalam try/catch — fallback ke input mentah jika dekoding gagal
app.get('/api/search', (req, res) => {
let query: string
try {
query = decodeURIComponent(req.query.q as string)
} catch {
query = req.query.q as string // gunakan nilai mentah daripada crash
}
// lanjutkan pemrosesan dengan aman — query sudah didekode atau mentah
})decodeURIComponent vs decodeURI vs URLSearchParams — Perbandingan Cepat
| Metode | Mendekode %XX | Mendekode + sebagai spasi | Melempar jika tidak valid | Mendekode & = ? # | Kasus penggunaan | Perlu instalasi |
|---|---|---|---|---|---|---|
| decodeURIComponent() | ✅ semua | ❌ tidak | ✅ URIError | ✅ ya | Nilai individual dan segmen jalur | No |
| decodeURI() | ✅ sebagian | ❌ tidak | ✅ URIError | ❌ tidak | String URL lengkap | No |
| URLSearchParams | ✅ semua | ✅ ya | ❌ diam-diam | ✅ ya | Penguraian query string dengan dekode otomatis | No |
| Konstruktor URL | ✅ semua | ✅ ya | ✅ TypeError | ✅ ya | Penguraian dan normalisasi URL lengkap | No |
| decode-uri-component | ✅ semua | ❌ tidak | ❌ diam-diam | ✅ ya | Dekode batch dengan toleransi input tidak valid | npm install |
| querystring.unescape() | ✅ semua | ❌ tidak | ❌ diam-diam | ✅ ya | Node.js lama (deprecated di v16) | No (bawaan) |
Untuk sebagian besar kasus, pilihan menyederhanakan menjadi tiga skenario. Gunakan URLSearchParams untuk mengurai query string — fungsi ini menangani dekoding, aturan +-sebagai-spasi, dan kunci berulang secara otomatis. Gunakan decodeURIComponent() (dibungkus dalam try/catch) untuk satu nilai atau segmen jalur, terutama ketika Anda mengharapkan garis miring yang disandikan %2F di dalam segmen. Gunakan decodeURI() hanya ketika Anda memiliki string URL lengkap dengan karakter non-ASCII dalam jalurnya dan memerlukan karakter struktural (/ ? & =) untuk tetap disandikan dalam output.
Pertanyaan yang Sering Diajukan
Alat Terkait
Untuk dekode satu klik tanpa menulis kode apa pun, tempel string yang disandikan persen Anda langsung ke Dekoder URL ToolDeck — fungsi ini mendekode secara instan di browser, dengan hasilnya siap untuk disalin ke kode atau terminal Anda.
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.