Next.js Integration
This guide shows you how to integrate IPBot into your Next.js application for geolocation, security, and personalization.
Overview
Section titled “Overview”Next.js offers multiple ways to integrate IPBot depending on your needs:
- Client-side: For simple lookups and personalization
- Server-side: For secure API calls and SSR
- API Routes: For proxying and custom endpoints
- Middleware: For edge-based redirects and blocking
Installation
Section titled “Installation”No package installation is required - IPBot is a REST API. However, you may want to add a fetch helper:
# Optional: Install isomorphic-unfetch for better Node/browser compatibilitynpm install isomorphic-unfetch# oryarn add isomorphic-unfetchMethod 1: Client-Side Integration
Section titled “Method 1: Client-Side Integration”For client-side IP lookup and personalization:
export interface IPBotResponse { ip: string; location: { country: string; country_code: string; region: string; city: string; postal: string; latitude: number; longitude: number; timezone: string; }; network: { asn: string; org: string; radar: "isp" | "datacenter" | "mobile" | "vpn" | "proxy" | "tor" | null; }; security: { risk_score: number; risk_reasons: string[]; usage_type: string; is_datacenter: boolean; is_proxy: boolean; threat_level: "Low" | "Medium" | "High"; threat_lists: string[]; }; telemetry: { user_agent: string; is_browser: boolean; }; meta: { process_time_us: number; data_version: string; engine: string; };}
export async function getIPInfo(ip?: string): Promise<IPBotResponse> { const url = ip ? `https://api.ipbot.com/${ip}` : "https://api.ipbot.com/"; const response = await fetch(url);
if (!response.ok) { throw new Error(`IPBot API error: ${response.statusText}`); }
return response.json();}React Hook
Section titled “React Hook”Create a custom hook for using IPBot in components:
import { useState, useEffect } from "react";import { getIPInfo, IPBotResponse } from "@/utils/ipbot";
export function useIPInfo(ip?: string) { const [data, setData] = useState<IPBotResponse | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<Error | null>(null);
useEffect(() => { getIPInfo(ip) .then(setData) .catch(setError) .finally(() => setLoading(false)); }, [ip]);
return { data, loading, error };}Usage in Component
Section titled “Usage in Component”import { useIPInfo } from "@/hooks/useIPInfo";
export function LocationBanner() { const { data, loading, error } = useIPInfo();
if (loading) return <div>Loading...</div>; if (error) return null; if (!data) return null;
return ( <div className="location-banner"> <p> Detected location: {data.location.city}, {data.location.country} </p> {data.security.risk_score > 50 && ( <p className="warning">Connecting from a high-risk location</p> )} </div> );}Method 2: Server-Side Integration (SSR)
Section titled “Method 2: Server-Side Integration (SSR)”For server-side rendering with IP data:
// pages/index.tsx or app/page.tsximport { GetServerSideProps } from 'next';import { IPBotResponse } from '@/utils/ipbot';
interface HomeProps { ipInfo: IPBotResponse | null; ipError?: string;}
export default function Home({ ipInfo, ipError }: HomeProps) { if (ipError) { return <div>Error loading location data</div>; }
return ( <main> {ipInfo && ( <div> <h1>Welcome from {ipInfo.location.city}!</h1> <p>Country: {ipInfo.location.country}</p> <p>ISP: {ipInfo.network.org}</p> </div> )} </main> );}
export const getServerSideProps: GetServerSideProps = async (context) => { // Get IP from headers (important for Vercel/CDN deployments) const ip = getIPFromRequest(context.req);
try { const response = await fetch(`https://api.ipbot.com/${ip}`); const ipInfo = await response.json();
return { props: { ipInfo, }, }; } catch (error) { return { props: { ipInfo: null, ipError: 'Failed to fetch IP info', }, }; }};
function getIPFromRequest(req: any): string { // Check various headers for the real IP return ( req.headers['x-forwarded-for']?.split(',')[0].trim() || req.headers['x-real-ip'] || req.headers['cf-connecting-ip'] || req.socket?.remoteAddress || 'unknown' );}Method 3: Next.js API Route Proxy
Section titled “Method 3: Next.js API Route Proxy”Create a proxy API route to hide the IPBot endpoint and add caching:
import type { NextApiRequest, NextApiResponse } from "next";
interface IPBotResponse { ip: string; location: any; security: any; // ... other fields}
export default async function handler( req: NextApiRequest, res: NextApiResponse,) { // Only allow GET requests if (req.method !== "GET") { return res.status(405).json({ error: "Method not allowed" }); }
const ip = req.query.ip as string | undefined;
try { // Get client IP if not provided const lookupIp = ip || getClientIP(req);
// Call IPBot API const response = await fetch(`https://api.ipbot.com/${lookupIp}`); const data: IPBotResponse = await response.json();
// Add CORS headers res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader( "Cache-Control", "public, s-maxage=86400, stale-while-revalidate=3600", );
return res.status(200).json(data); } catch (error) { console.error("IPBot API error:", error); return res.status(500).json({ error: "Failed to fetch IP information" }); }}
function getClientIP(req: NextApiRequest): string { return ( (req.headers["x-forwarded-for"] as string)?.split(",")[0].trim() || (req.headers["x-real-ip"] as string) || (req.headers["cf-connecting-ip"] as string) || req.socket.remoteAddress || "unknown" );}Method 4: Next.js Middleware (Edge Runtime)
Section titled “Method 4: Next.js Middleware (Edge Runtime)”Use middleware for location-based redirects and blocking at the edge:
import { NextResponse } from "next/server";import type { NextRequest } from "next/server";
const BLOCKED_COUNTRIES = ["CN", "RU", "KP"];const MIN_RISK_SCORE = 70;
export async function middleware(request: NextRequest) { const ip = getClientIP(request);
// Skip for static assets and API health checks if ( request.nextUrl.pathname.startsWith("/_next") || request.nextUrl.pathname.startsWith("/api/health") ) { return NextResponse.next(); }
try { // Call IPBot API const response = await fetch(`https://api.ipbot.com/${ip}`, { // Use edge runtime compatible fetch options });
if (!response.ok) { return NextResponse.next(); }
const data = await response.json(); const countryCode = data.location?.country_code; const riskScore = data.security?.risk_score;
// Block based on country if (countryCode && BLOCKED_COUNTRIES.includes(countryCode)) { return new Response("Access denied from your location", { status: 403 }); }
// Block based on risk score if (riskScore && riskScore > MIN_RISK_SCORE) { return new Response("Access denied due to security concerns", { status: 403, }); }
// Add location info to headers for use in pages const responseHeaders = new Headers(request.headers); responseHeaders.set("x-country", countryCode || "unknown"); responseHeaders.set("x-risk-score", riskScore?.toString() || "0");
return NextResponse.next({ request: { headers: responseHeaders, }, }); } catch (error) { // On error, allow the request to proceed return NextResponse.next(); }}
function getClientIP(request: NextRequest): string { return ( request.headers.get("cf-connecting-ip") || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || "unknown" );}
// Configure which routes the middleware runs onexport const config = { matcher: [ /* * Match all request paths except: * - api routes that handle their own auth * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) */ "/((?!api/health|_next/static|_next/image|favicon.ico).*)", ],};Method 5: App Router (Next.js 13+) Server Actions
Section titled “Method 5: App Router (Next.js 13+) Server Actions”For the App Router, use Server Actions:
"use server";
import { IPBotResponse } from "@/utils/ipbot";
export async function getServerIPInfo(): Promise<IPBotResponse> { const response = await fetch("https://api.ipbot.com/", { cache: "no-store", // Or use Next.js revalidate });
if (!response.ok) { throw new Error("Failed to fetch IP info"); }
return response.json();}import { getServerIPInfo } from "./actions";
export default async function Home() { const ipInfo = await getServerIPInfo();
return ( <main> <h1>Welcome from {ipInfo.location.city}!</h1> <p>Country: {ipInfo.location.country}</p> </main> );}Caching Strategy
Section titled “Caching Strategy”Implement proper caching to minimize API calls:
Using Next.js Data Cache (App Router)
Section titled “Using Next.js Data Cache (App Router)”export async function getIPInfoWithCache(ip: string) { const response = await fetch(`https://api.ipbot.com/${ip}`, { next: { revalidate: 3600, // Revalidate every hour tags: ["ip-info", `ip-${ip}`], }, });
return response.json();}Using React Query (Client-side)
Section titled “Using React Query (Client-side)”import { useQuery } from "@tanstack/react-query";
export function useIPInfo(ip?: string) { return useQuery({ queryKey: ["ip-info", ip], queryFn: () => getIPInfo(ip), staleTime: 1000 * 60 * 60, // 1 hour cacheTime: 1000 * 60 * 60 * 24, // 24 hours });}Deployment Considerations
Section titled “Deployment Considerations”Vercel
Section titled “Vercel”Vercel automatically sets the x-forwarded-for header:
const ip = req.headers["x-forwarded-for"] as string;Cloudflare
Section titled “Cloudflare”Cloudflare sets the cf-connecting-ip header:
const ip = req.headers["cf-connecting-ip"] as string;Self-hosted
Section titled “Self-hosted”For self-hosted Next.js behind a reverse proxy, configure proxy headers:
const ip = (req.headers["x-real-ip"] as string) || req.socket.remoteAddress;Complete Example: Location-Based Content
Section titled “Complete Example: Location-Based Content”"use client";
import { useIPInfo } from "@/hooks/useIPInfo";
export function LocalizedContent() { const { data, loading } = useIPInfo();
if (loading) return <div>Loading...</div>;
const countryCode = data?.location?.country_code;
return ( <div> {countryCode === "US" && ( <div> <h2>Welcome to our US store!</h2> <p>Free shipping on orders over $50.</p> </div> )}
{countryCode === "GB" && ( <div> <h2>Welcome to our UK store!</h2> <p>Free shipping on orders over 35.</p> </div> )}
{!["US", "GB"].includes(countryCode || "") && ( <div> <h2>International shipping available</h2> <p>We ship worldwide from our locations.</p> </div> )} </div> );}Troubleshooting
Section titled “Troubleshooting”Issue: CORS errors in development
Section titled “Issue: CORS errors in development”Solution: Next.js API routes handle CORS automatically. Use the proxy method above.
Issue: Wrong IP detected
Section titled “Issue: Wrong IP detected”Solution: Check headers in order of priority for your hosting setup.
Issue: Rate limiting
Section titled “Issue: Rate limiting”Solution: Implement proper caching and use Next.js revalidation.