[TERMINAL · SKILLS]
> mounting /skills...
> indexing 295 manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Terminal.skills
Use Cases/Build Geolocation-Based Service Routing

Build Geolocation-Based Service Routing

Build a location-aware routing system that directs users to the nearest service, applies regional pricing, enforces geo-restrictions, and personalizes content based on IP geolocation.

#redis#caching#database#pub-sub#queues
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 93/100·All SAFE
>

typescript

v

Not yet scored
View skill
>

redis

v1.0.0

Build applications with Redis — caching, session storage, pub/sub, streams, rate limiting, leaderboards, and queues. Use when tasks involve in-memory data storage, real-time messaging, distributed locking, or performance optimization with caching layers.

93/100 quality
1.81× impact
SAFE
View skill
>

hono

v1.0.0

You are an expert in Hono, the ultrafast web framework for the edge. You help developers build APIs and web applications that run on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge — with a tiny footprint (~14KB), middleware ecosystem, JSX support, RPC client, and Web Standards API compatibility that makes code truly portable across runtimes.

93/100 quality
3.00× impact
SAFE
View skill
>

zod

v1.0.0

You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.

100/100 quality
1.21× impact
SAFE
View skill
>

postgresql

v1.0.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

87/100 quality
1.53× impact
SAFE
View skill
$

The Problem

Sonya runs a 35-person SaaS serving customers in 60 countries. All traffic routes to the same server regardless of location. Pricing shows USD for everyone, even Europeans who expect EUR. Data residency laws require EU customer data to stay in EU servers, but the app has no location awareness. When they launched in Brazil, they discovered their service was unusably slow because traffic routed through 3 continents. They need geolocation-based routing that detects user location, serves localized pricing, enforces data residency, and routes to the nearest server.

Step 1: Build the Geolocation Service

typescript
// src/geo/geo-service.ts — IP geolocation with caching and routing decisions
import { Redis } from "ioredis";
import { readFileSync } from "node:fs";

const redis = new Redis(process.env.REDIS_URL!);

interface GeoInfo {
  ip: string;
  country: string;           // ISO 3166-1 alpha-2
  countryName: string;
  region: string;
  city: string;
  latitude: number;
  longitude: number;
  timezone: string;
  currency: string;
  continent: string;
  isEU: boolean;
}

// Regional server configuration
const REGIONS: Record<string, { name: string; endpoint: string; countries: string[] }> = {
  "us-east": {
    name: "US East",
    endpoint: "https://us-east.api.example.com",
    countries: ["US", "CA", "MX", "BR", "AR", "CO"],
  },
  "eu-west": {
    name: "EU West",
    endpoint: "https://eu-west.api.example.com",
    countries: ["DE", "FR", "GB", "IT", "ES", "NL", "BE", "AT", "CH", "PL", "CZ", "SE", "NO", "DK", "FI", "IE", "PT"],
  },
  "ap-southeast": {
    name: "Asia Pacific",
    endpoint: "https://ap-southeast.api.example.com",
    countries: ["JP", "KR", "SG", "AU", "NZ", "IN", "TH", "VN", "ID", "MY", "PH"],
  },
};

// Regional pricing
const PRICING: Record<string, { currency: string; multiplier: number; symbol: string }> = {
  US: { currency: "USD", multiplier: 1.0, symbol: "$" },
  GB: { currency: "GBP", multiplier: 0.82, symbol: "£" },
  EU: { currency: "EUR", multiplier: 0.92, symbol: "€" },
  JP: { currency: "JPY", multiplier: 150, symbol: "¥" },
  BR: { currency: "BRL", multiplier: 0.5, symbol: "R$" },   // PPP adjusted
  IN: { currency: "INR", multiplier: 0.3, symbol: "₹" },   // PPP adjusted
};

// Geo-restricted content
const BLOCKED_COUNTRIES = ["KP", "IR", "SY", "CU"]; // sanctions compliance

// Get geo info from IP
export async function getGeoInfo(ip: string): Promise<GeoInfo> {
  // Check cache
  const cached = await redis.get(`geo:${ip}`);
  if (cached) return JSON.parse(cached);

  // Use Cloudflare headers first (free, accurate)
  // Fallback to MaxMind GeoLite2 database
  const info = await lookupIP(ip);

  // Cache for 24 hours
  await redis.setex(`geo:${ip}`, 86400, JSON.stringify(info));
  return info;
}

// Determine which regional server to route to
export function getRoutingRegion(country: string): {
  region: string;
  endpoint: string;
  name: string;
} {
  for (const [regionId, config] of Object.entries(REGIONS)) {
    if (config.countries.includes(country)) {
      return { region: regionId, endpoint: config.endpoint, name: config.name };
    }
  }
  return { region: "us-east", endpoint: REGIONS["us-east"].endpoint, name: "US East (default)" };
}

// Get localized pricing
export function getLocalizedPricing(
  country: string,
  basePriceUSD: number
): { amount: number; currency: string; symbol: string; formatted: string } {
  // Check EU membership
  const isEU = ["DE", "FR", "IT", "ES", "NL", "BE", "AT", "PT", "FI", "IE", "GR", "LU", "CY", "MT", "SK", "SI", "EE", "LV", "LT", "HR", "BG", "RO", "CZ", "DK", "SE", "PL", "HU"].includes(country);
  const pricingKey = isEU ? "EU" : country;
  const pricing = PRICING[pricingKey] || PRICING["US"];

  const amount = Math.round(basePriceUSD * pricing.multiplier * 100) / 100;

  return {
    amount,
    currency: pricing.currency,
    symbol: pricing.symbol,
    formatted: `${pricing.symbol}${amount.toLocaleString()}`,
  };
}

// Check compliance restrictions
export function checkGeoRestrictions(country: string): {
  allowed: boolean;
  reason?: string;
  dataResidency?: string;
} {
  if (BLOCKED_COUNTRIES.includes(country)) {
    return { allowed: false, reason: "Service not available in this region due to compliance requirements" };
  }

  // GDPR data residency for EU
  const isEU = ["DE", "FR", "IT", "ES", "NL", "BE", "AT", "PT", "FI", "IE"].includes(country);
  if (isEU) {
    return { allowed: true, dataResidency: "eu-west" };
  }

  return { allowed: true };
}

async function lookupIP(ip: string): Promise<GeoInfo> {
  // In production: use MaxMind GeoLite2 or ip-api.com
  const response = await fetch(`http://ip-api.com/json/${ip}?fields=status,country,countryCode,region,city,lat,lon,timezone,currency,continent`);
  const data = await response.json();

  return {
    ip,
    country: data.countryCode,
    countryName: data.country,
    region: data.region,
    city: data.city,
    latitude: data.lat,
    longitude: data.lon,
    timezone: data.timezone,
    currency: data.currency || "USD",
    continent: data.continent || "",
    isEU: ["DE", "FR", "IT", "ES", "NL", "BE", "AT"].includes(data.countryCode),
  };
}

Step 2: Geo-Aware Middleware

typescript
// src/middleware/geo.ts — Geolocation middleware for Hono
import { Context, Next } from "hono";
import { getGeoInfo, checkGeoRestrictions, getRoutingRegion, getLocalizedPricing } from "../geo/geo-service";

export function geoMiddleware() {
  return async (c: Context, next: Next) => {
    // Get IP from headers (CDN/proxy sets these)
    const ip = c.req.header("CF-Connecting-IP")
      || c.req.header("X-Forwarded-For")?.split(",")[0]?.trim()
      || "127.0.0.1";

    // Use Cloudflare's geo header if available (zero-cost, instant)
    const country = c.req.header("CF-IPCountry") || (await getGeoInfo(ip)).country;

    // Check restrictions
    const restrictions = checkGeoRestrictions(country);
    if (!restrictions.allowed) {
      return c.json({ error: restrictions.reason }, 403);
    }

    // Set context for downstream handlers
    c.set("geoCountry", country);
    c.set("geoDataResidency", restrictions.dataResidency || "us-east");
    c.set("geoRegion", getRoutingRegion(country));

    // Add geo headers for client-side use
    c.header("X-Geo-Country", country);
    c.header("X-Data-Region", restrictions.dataResidency || "us-east");

    await next();
  };
}

// Pricing endpoint with geo-aware pricing
export function pricingHandler() {
  return async (c: Context) => {
    const country = c.get("geoCountry");
    const basePrices = { starter: 29, pro: 99, enterprise: 499 };

    const plans = Object.entries(basePrices).map(([plan, basePrice]) => ({
      plan,
      ...getLocalizedPricing(country, basePrice),
    }));

    return c.json({ country, plans });
  };
}

Results

  • Brazilian latency: 350ms → 45ms — routing to the nearest regional server eliminated cross-continental round trips
  • EU data residency compliance — geo middleware routes EU user data to eu-west servers automatically; GDPR auditors verified compliance in one meeting
  • Localized pricing increased conversion by 28% — Brazilian users see prices in BRL with PPP adjustment; Indian users see INR; no more "everything is in USD" barrier
  • Sanctions compliance automated — blocked countries get a 403 response; no manual blocklist maintenance in each service
  • Zero additional latency for geo detection — Cloudflare's CF-IPCountry header provides instant geolocation; IP-based lookup cached for 24 hours as fallback