Watch-IP

Geolokasi IP di Next.js: pola browser dan server

Next.js bisa me-render halaman yang sama di server atau di browser, dan pilihan itu mengubah endpoint Watch-IP mana yang sebenarnya menyelesaikan lokasi pengunjung — atau justru menyelesaikan lokasi server Anda.

Sebelum Anda mulai

  • Publishable visitor key untuk pola client, atau secret lookup key untuk pola server — lihat autentikasi.
  • Proyek Next.js App Router (contoh mengasumsikan pembagian Server/Client Component Next.js 16).
  • Untuk pola server, sebuah header yang diset platform hosting Anda pada request masuk berisi IP asli pengunjung (misalnya CF-Connecting-IP di Cloudflare) — bukan header yang bisa diset sendiri oleh klien.

1. Pahami apa yang sebenarnya dipanggil setiap pola

GET /v1/geo mengidentifikasi koneksi siapa pun yang mencapai API — browser pengunjung yang memanggilnya langsung, atau server Next.js Anda jika kode Anda sendiri memanggilnya saat server rendering. Memanggil GET /v1/geo dari Server Component atau Route Handler akan menyelesaikan lokasi jaringan server Anda, bukan pengunjung, yang biasanya bukan yang Anda inginkan. Dua pola yang benar-benar menyelesaikan lokasi pengunjung: jalankan request di browser (Client Component), atau jalankan di server Anda terhadap IP yang dikirim dengan GET /v1/lookup/{ip}.

2. Client Component: panggil GET /v1/geo dari browser

Ini adalah request yang sama yang digunakan panduan JavaScript dan React — sebuah Client Component yang melakukan fetch dengan publishable key. Header Origin milik browser itu sendiri memenuhi pemeriksaan allowed-origins key tersebut, jadi URL publik aplikasi Anda perlu ada di daftar itu.

TypeScript (Client Component)

"use client";

import { useEffect, useState } from "react";

export function VisitorLocation() {
  const [city, setCity] = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();

    fetch("https://api.watch-ip.com/v1/geo", {
      headers: { "X-Api-Key": "YOUR_PUBLISHABLE_KEY" },
      signal: controller.signal,
    })
      .then((res) => (res.ok ? res.json() : null))
      .then((geo) => setCity(geo?.city ?? null))
      .catch(() => {});

    return () => controller.abort();
  }, []);

  return <span>{city ?? "wilayah ini"}</span>;
}

3. Server Component atau Route Handler: cari IP pengunjung secara eksplisit

Untuk menyelesaikan lokasi pengunjung saat server rendering, jangan panggil GET /v1/geo — panggil GET /v1/lookup/{ip} dengan secret key dan IP asli pengunjung, dibaca dari header mana pun yang diset platform hosting Anda pada request masuk. Jangan pernah memercayai nilai X-Forwarded-For biasa yang bisa diset sendiri oleh klien kecuali platform Anda menghapus dan mengesetnya ulang di edge.

TypeScript (Server Component)

import { headers } from "next/headers";

export async function VisitorLocationServer() {
  const ip = (await headers()).get("cf-connecting-ip");
  if (!ip) return null;

  const response = await fetch(`https://api.watch-ip.com/v1/lookup/${ip}`, {
    headers: { Authorization: "Bearer YOUR_SECRET_KEY" },
    cache: "no-store",
  });
  if (!response.ok) return null;

  const geo = await response.json();
  return <span>{geo.city ?? "wilayah ini"}</span>;
}

4. Jangan meng-cache hasil satu pengunjung untuk pengunjung lain

cache: 'no-store' di atas memang disengaja: hasil pencarian bersifat khusus untuk pengunjung yang memintanya. Jika komponen ini dirender di dalam rute yang bisa di-cache oleh Next.js atau CDN — halaman statis, halaman ISR, fetch cache bersama — respons yang di-cache akan menampilkan kota satu pengunjung ke semua orang setelahnya. Jaga agar cabang yang dipersonalisasi tetap dirender secara dinamis, dan jangan pernah melipat respons pencarian ke dalam cache tingkat halaman atau tingkat CDN.

Error dan kasus khusus

Header IP yang hilang atau kosong (beberapa setup lokal atau proxy tidak mengesetnya) berarti tidak ada IP pengunjung untuk dicari — render fallback-nya, bukan memanggil endpoint dengan string kosong. 403 dari GET /v1/lookup/{ip} biasanya berarti publishable key digunakan padahal rute ini butuh secret key; 400 berarti header tersebut berisi sesuatu yang bukan alamat IPv4/IPv6 yang valid. Lihat error dan rate limit.

Langkah berikutnya

Untuk komponen yang bisa dipakai ulang di aplikasi React client-only maupun Next.js, lihat panduan React — pola Client Component di atas adalah pembungkus khusus Next.js di sekitar request yang sama.

Halaman terkait