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
The IP address of the client making the request.Example: "203.0.113.42"
The country name or country code associated with the IP address.Example: "United States", "US"
The region, state, or province associated with the IP address.Example: "California", "CA"
The city associated with the IP address.Example: "San Francisco"
The timezone of the location associated with the IP address.Example: "America/Los_Angeles", "UTC-08:00"
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
};
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}`);
}
}
}
};
Popular IP Geolocation Services
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