Skip to main content

Overview

The IpInfo interface represents IP geolocation information that can be added to logs. It provides details about the geographic location, timezone, and other metadata associated with a client’s IP address. This information is populated when the getIpInfo function is provided in the middleware options.

Interface Definition

interface IpInfo {
  ip?: string;
  country?: string;
  region?: string;
  city?: string;
  timezone?: string;
  [key: string]: unknown;
}

Properties

ip
string
The IP address of the client making the request.Example: "203.0.113.42"
country
string
The country name or country code associated with the IP address.Example: "United States", "US"
region
string
The region, state, or province associated with the IP address.Example: "California", "CA"
city
string
The city associated with the IP address.Example: "San Francisco"
timezone
string
The timezone of the location associated with the IP address.Example: "America/Los_Angeles", "UTC-08:00"
[key: string]
unknown
Additional custom properties can be included based on your IP geolocation service. The interface uses an index signature to allow any additional fields.Common additional fields:
  • latitude - Geographic latitude
  • longitude - Geographic longitude
  • postal - Postal/ZIP code
  • org - Organization name (ISP)
  • asn - Autonomous System Number
  • continent - Continent name

Usage Examples

Basic IP Info Provider

import { httpLedger, ApiLoggerOptions, IpInfo } from '@httpledger/express';

const options: ApiLoggerOptions = {
  getIpInfo: async (ip: string): Promise<IpInfo> => {
    try {
      const response = await fetch(`https://ipapi.co/${ip}/json/`);
      const data = await response.json();
      
      return {
        ip: data.ip,
        country: data.country_name,
        region: data.region,
        city: data.city,
        timezone: data.timezone
      };
    } catch (error) {
      // Return minimal info on error
      return { ip };
    }
  }
};

app.use(httpLedger(options));

Extended IP Info with Additional Fields

import { IpInfo } from '@httpledger/express';

interface ExtendedIpInfo extends IpInfo {
  latitude?: number;
  longitude?: number;
  postal?: string;
  org?: string;
  asn?: string;
}

const options: ApiLoggerOptions = {
  getIpInfo: async (ip: string): Promise<ExtendedIpInfo> => {
    const response = await fetch(`https://ipapi.co/${ip}/json/`);
    const data = await response.json();
    
    return {
      ip: data.ip,
      country: data.country_name,
      region: data.region,
      city: data.city,
      timezone: data.timezone,
      latitude: data.latitude,
      longitude: data.longitude,
      postal: data.postal,
      org: data.org,
      asn: data.asn
    };
  }
};

Using Multiple IP Geolocation Services

import { IpInfo } from '@httpledger/express';

async function getIpInfoWithFallback(ip: string): Promise<IpInfo> {
  // Try primary service
  try {
    const response = await fetch(`https://ipapi.co/${ip}/json/`, {
      timeout: 2000
    });
    
    if (response.ok) {
      const data = await response.json();
      return {
        ip: data.ip,
        country: data.country_name,
        region: data.region,
        city: data.city,
        timezone: data.timezone
      };
    }
  } catch (error) {
    console.warn('Primary IP service failed, trying fallback');
  }
  
  // Try fallback service
  try {
    const response = await fetch(`https://ip-api.com/json/${ip}`);
    const data = await response.json();
    
    return {
      ip: data.query,
      country: data.country,
      region: data.regionName,
      city: data.city,
      timezone: data.timezone
    };
  } catch (error) {
    // Return minimal info on complete failure
    return { ip };
  }
}

const options: ApiLoggerOptions = {
  getIpInfo: getIpInfoWithFallback
};

Caching IP Info for Performance

import { IpInfo } from '@httpledger/express';
import NodeCache from 'node-cache';

// Cache IP info for 24 hours
const ipCache = new NodeCache({ stdTTL: 86400 });

const options: ApiLoggerOptions = {
  getIpInfo: async (ip: string): Promise<IpInfo> => {
    // Check cache first
    const cached = ipCache.get<IpInfo>(ip);
    if (cached) {
      return cached;
    }
    
    // Fetch from service
    try {
      const response = await fetch(`https://ipapi.co/${ip}/json/`);
      const data = await response.json();
      
      const ipInfo: IpInfo = {
        ip: data.ip,
        country: data.country_name,
        region: data.region,
        city: data.city,
        timezone: data.timezone
      };
      
      // Store in cache
      ipCache.set(ip, ipInfo);
      
      return ipInfo;
    } catch (error) {
      return { ip };
    }
  }
};

Privacy-Conscious IP Info

import { IpInfo } from '@httpledger/express';

const options: ApiLoggerOptions = {
  getIpInfo: async (ip: string): Promise<IpInfo> => {
    try {
      const response = await fetch(`https://ipapi.co/${ip}/json/`);
      const data = await response.json();
      
      // Only include country-level info for privacy
      return {
        // Don't include the actual IP
        country: data.country_name,
        timezone: data.timezone
        // Exclude city, region, and other precise location data
      };
    } catch (error) {
      return {};
    }
  }
};

Using IP Info in Log Analysis

import { LogData, IpInfo } from '@httpledger/express';

const options: ApiLoggerOptions = {
  getIpInfo: async (ip: string): Promise<IpInfo> => {
    // ... fetch IP info
  },
  
  onLog: async (logData: LogData) => {
    if (logData.ipInfo) {
      // Analyze traffic by country
      console.log(`Request from ${logData.ipInfo.country}`);
      
      // Track regional performance
      if (logData.ipInfo.region && logData.timeTaken > 5000) {
        console.warn(`Slow request in ${logData.ipInfo.region}:`, {
          url: logData.url,
          duration: logData.timeTaken
        });
      }
      
      // Time zone aware logging
      if (logData.ipInfo.timezone) {
        const localTime = new Date().toLocaleString('en-US', {
          timeZone: logData.ipInfo.timezone
        });
        console.log(`Local time for user: ${localTime}`);
      }
    }
  }
};

ipapi.co

getIpInfo: async (ip: string): Promise<IpInfo> => {
  const response = await fetch(`https://ipapi.co/${ip}/json/`);
  const data = await response.json();
  return {
    ip: data.ip,
    country: data.country_name,
    region: data.region,
    city: data.city,
    timezone: data.timezone
  };
}

ip-api.com

getIpInfo: async (ip: string): Promise<IpInfo> => {
  const response = await fetch(`http://ip-api.com/json/${ip}`);
  const data = await response.json();
  return {
    ip: data.query,
    country: data.country,
    region: data.regionName,
    city: data.city,
    timezone: data.timezone
  };
}

ipinfo.io

getIpInfo: async (ip: string): Promise<IpInfo> => {
  const token = process.env.IPINFO_TOKEN;
  const response = await fetch(`https://ipinfo.io/${ip}?token=${token}`);
  const data = await response.json();
  return {
    ip: data.ip,
    country: data.country,
    region: data.region,
    city: data.city,
    timezone: data.timezone
  };
}

Example IpInfo Object

{
  "ip": "203.0.113.42",
  "country": "United States",
  "region": "California",
  "city": "San Francisco",
  "timezone": "America/Los_Angeles",
  "latitude": 37.7749,
  "longitude": -122.4194,
  "postal": "94102",
  "org": "Example ISP",
  "asn": "AS15169"
}

Best Practices

1. Handle Errors Gracefully

Always return at least a minimal IpInfo object, even on errors:
try {
  // Fetch IP info
} catch (error) {
  return { ip }; // Always return something
}

2. Implement Caching

Cache IP info to avoid repeated lookups for the same IP:
const cache = new Map<string, IpInfo>();

3. Set Timeouts

Don’t let IP lookups slow down your application:
const response = await fetch(url, { timeout: 2000 });

4. Consider Privacy

Only collect location data you actually need:
// Country-level may be sufficient
return { country: data.country };

5. Handle Private IPs

Check for private/local IP addresses:
function isPrivateIP(ip: string): boolean {
  return ip.startsWith('10.') || 
         ip.startsWith('192.168.') || 
         ip.startsWith('127.');
}

See Also

  • ApiLoggerOptions - Middleware configuration including getIpInfo
  • LogData - The complete log data structure that includes ipInfo

Build docs developers (and LLMs) love