Geolocation

Detect user location with IPinfo for dynamic pricing and localized experiences

This template includes IP geolocation powered by IPinfo, enabling you to detect user location for features like dynamic pricing, localized content, and country-specific experiences.

Overview

The geolocation integration provides:

  • IP-based location detection - Country, city, region, timezone.
  • Two API endpoints - Get current user's location or query specific IPs.
  • Server-side processing - Secure API key management.
  • Privacy-focused - No personal data storage required.

Configuration

Add your IPinfo API token to .env:

.env
IPINFO_TOKEN="your_ipinfo_token"
Sign up at IPinfo to get your token. The free tier (IPinfo Lite) includes unlimited API requests with 7 essential IP attributes (country, continent, ASN). Paid plans start at $49/month for city-level accuracy and privacy detection.

API endpoints

The template includes two endpoints:

/api/ip - Returns IP info for the current request

Returns: { ip, hostname, city, region, country, loc, org, postal, timezone }

/api/ip/[ipAddress] - Returns IP info for a specific IP address

Example: /api/ip/8.8.8.8

Both endpoints use getIpInfo() from server/services/ip-server-service.ts.

Usage examples

Get current user's location

<script setup>
const { data: ipInfo } = await useFetch('/api/ip')
</script>

<template>
  <div>
    <p>Country: {{ ipInfo?.country }}</p>
    <p>City: {{ ipInfo?.city }}</p>
    <p>Timezone: {{ ipInfo?.timezone }}</p>
  </div>
</template>

Dynamic pricing by country

Detect user's country and show appropriate currency:

<script setup>
const { data: ipInfo } = await useFetch('/api/ip')

const currency = computed(() => {
  const country = ipInfo.value?.country
  if (country === 'GB') return 'GBP'
  if (['DE', 'FR', 'ES', 'IT'].includes(country)) return 'EUR'
  return 'USD'
})

const price = computed(() => {
  const prices = { USD: 29, EUR: 25, GBP: 22 }
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: currency.value,
  }).format(prices[currency.value])
})
</script>

<template>
  <div>{{ price }}/month</div>
</template>

Stripe checkout with location-based pricing

Pass the user's country to your checkout endpoint:

server/api/stripe/checkout-session.post.ts
export default defineEventHandler(async event => {
  const { plan, interval } = await readBody(event)
  
  // Get user's location
  const ipInfo = await getIpInfo()
  
  // Map country to Stripe price ID
  // You'll need to create price IDs for each currency in Stripe
  const priceId = getPriceIdForCountry(plan, interval, ipInfo.country)
  
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    line_items: [{ price: priceId, quantity: 1 }],
    // ...
  })

  return { url: session.url }
})

Additional use cases

Localized content

Show different content or messages based on user's country:

<script setup>
const { data: ipInfo } = await useFetch('/api/ip')

const welcomeMessage = computed(() => {
  const messages = {
    US: 'Welcome! 🇺🇸',
    DE: 'Willkommen! 🇩🇪',
    FR: 'Bienvenue! 🇫🇷',
  }
  return messages[ipInfo.value?.country] || 'Welcome!'
})
</script>

Regional restrictions

Restrict access based on location in server middleware:

server/middleware/geo-check.ts
export default defineEventHandler(async event => {
  const ipInfo = await getIpInfo()
  
  const blockedCountries = ['XX', 'YY']
  if (blockedCountries.includes(ipInfo.country)) {
    throw createError({
      statusCode: 403,
      message: 'Service not available in your region',
    })
  }
})

User preference override

Allow users to manually select their currency/location:

<script setup>
const { data: ipInfo } = await useFetch('/api/ip')
const userCurrency = useCookie('user-currency')

const currency = computed({
  get: () => userCurrency.value || getCurrencyFromCountry(ipInfo.value?.country),
  set: (value) => { userCurrency.value = value }
})
</script>

<template>
  <Select v-model="currency">
    <SelectItem value="USD">USD</SelectItem>
    <SelectItem value="EUR">EUR</SelectItem>
    <SelectItem value="GBP">GBP</SelectItem>
  </Select>
</template>

Privacy and performance

GDPR compliance: IP-based geolocation is generally acceptable under GDPR as IPs are used for technical purposes. Include geolocation usage in your privacy policy.

Caching: The free tier provides unlimited API requests, but consider caching IP lookups to improve performance and reduce latency.

Error handling: Always provide fallback values (e.g., default to 'US' or 'USD') if geolocation fails.

Reference

Always provide fallback values (default country/currency) in case geolocation fails due to network issues or missing API token configuration.