Complete examples demonstrating how to use the Geo API SDK.
- Basic Setup
- Authentication Flow
- Geography Queries
- Account Management
- API Keys Lifecycle
- Subscription Management
- Webhook Setup
- Error Handling Patterns
- Production Best Practices
import { ApogeoAPI } from 'apogeoapi';
import express from 'express';
const app = express();
const client = new ApogeoAPI({
apiKey: process.env.GEO_API_KEY!,
baseURL: process.env.GEO_API_URL || 'https://api.apogeoapi.com/v1',
timeout: 30000,
retries: 3
});
app.get('/countries', async (req, res) => {
try {
const countries = await client.geo.getCountries();
res.json(countries);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);// pages/api/countries.ts
import { ApogeoAPI } from 'apogeoapi';
import type { NextApiRequest, NextApiResponse } from 'next';
const client = new ApogeoAPI({
apiKey: process.env.GEO_API_KEY!
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const countries = await client.geo.getCountries();
res.status(200).json(countries);
} catch (error: any) {
res.status(error.statusCode || 500).json({
error: error.message
});
}
}// hooks/useGeoAPI.ts
import { ApogeoAPI } from 'apogeoapi';
import { useState, useEffect } from 'react';
const client = new ApogeoAPI({
apiKey: process.env.NEXT_PUBLIC_GEO_API_KEY!
});
export function useCountries() {
const [countries, setCountries] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
client.geo.getCountries()
.then(setCountries)
.catch(setError)
.finally(() => setLoading(false));
}, []);
return { countries, loading, error };
}
// Usage in component
function CountriesList() {
const { countries, loading, error } = useCountries();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{countries.map(country => (
<li key={country.id}>{country.name}</li>
))}
</ul>
);
}import { ApogeoAPI, ApogeoAPIError } from 'apogeoapi';
// Initialize without auth (for registration)
const publicClient = new ApogeoAPI({
apiKey: 'public_api_key' // Or omit for public endpoints
});
async function registerAndLogin() {
try {
// 1. Register
const registration = await publicClient.auth.register({
email: 'user@example.com',
password: 'SecurePassword123!',
username: 'johndoe'
});
console.log('User registered:', registration.user);
console.log('Access token:', registration.access_token);
// 2. Create new client with token
const authenticatedClient = new ApogeoAPI({
token: registration.access_token
});
// 3. Verify profile
const profile = await authenticatedClient.auth.getProfile();
console.log('Profile:', profile);
// 4. Store tokens securely
localStorage.setItem('access_token', registration.access_token);
localStorage.setItem('refresh_token', registration.refresh_token);
return authenticatedClient;
} catch (error) {
if (error instanceof ApogeoAPIError) {
if (error.statusCode === 409) {
console.error('Email already exists');
} else {
console.error('Registration failed:', error.message);
}
}
throw error;
}
}
// Token refresh helper
async function refreshAuthToken(refreshToken: string) {
const client = new ApogeoAPI({ token: 'temp' });
try {
const newTokens = await client.auth.refreshToken({
refresh_token: refreshToken
});
localStorage.setItem('access_token', newTokens.access_token);
localStorage.setItem('refresh_token', newTokens.refresh_token);
return newTokens.access_token;
} catch (error) {
// Refresh failed, redirect to login
localStorage.clear();
window.location.href = '/login';
}
}import { ApogeoAPI, Country, State, City } from 'apogeoapi';
const client = new ApogeoAPI({ apiKey: process.env.GEO_API_KEY! });
class LocationSelector {
async getCountries(): Promise<Country[]> {
return client.geo.getCountries();
}
async getStatesForCountry(countryIso2: string): Promise<State[]> {
return client.geo.getStates(countryIso2);
}
async getCitiesForState(stateId: number): Promise<City[]> {
return client.geo.getCities(stateId, 1000); // Get up to 1000 cities
}
async searchLocation(query: string) {
// Search across all types
const results = await client.geo.search({
query,
limit: 10
});
return {
countries: results.data.filter(r => r.type === 'country'),
states: results.data.filter(r => r.type === 'state'),
cities: results.data.filter(r => r.type === 'city')
};
}
}
// Usage
const selector = new LocationSelector();
// Cascade dropdowns
const countries = await selector.getCountries();
const states = await selector.getStatesForCountry('US');
const cities = await selector.getCitiesForState(1234);
// Search as user types
const results = await selector.searchLocation('new');
console.log(results); // { countries: [...], states: [...], cities: [...] }import { ApogeoAPI, Country } from 'apogeoapi';
class CachedGeoClient {
private cache = new Map<string, any>();
private ttl = 24 * 60 * 60 * 1000; // 24 hours
constructor(private client: GeoAPI) {}
async getCountries(): Promise<Country[]> {
const cacheKey = 'countries';
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const data = await this.client.geo.getCountries();
this.cache.set(cacheKey, { data, timestamp: Date.now() });
return data;
}
async getCountryByIso(iso2: string): Promise<Country> {
const cacheKey = `country:${iso2}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const data = await this.client.geo.getCountryByIso(iso2);
this.cache.set(cacheKey, { data, timestamp: Date.now() });
return data;
}
clearCache() {
this.cache.clear();
}
}
// Usage
const client = new ApogeoAPI({ apiKey: 'xxx' });
const cachedClient = new CachedGeoClient(client);
const countries = await cachedClient.getCountries(); // Fetches from API
const countriesAgain = await cachedClient.getCountries(); // Returns from cacheimport { ApogeoAPI } from 'apogeoapi';
const client = new ApogeoAPI({ apiKey: 'xxx' });
async function buildDashboard() {
const dashboard = await client.account.getDashboard();
const usagePercentage = (
(dashboard.usage.requestsThisMonth / dashboard.usage.monthlyQuota) * 100
).toFixed(2);
console.log('='.repeat(50));
console.log('ACCOUNT DASHBOARD');
console.log('='.repeat(50));
console.log(`User: ${dashboard.user.username} (${dashboard.user.email})`);
console.log(`Plan: ${dashboard.subscription.tier.toUpperCase()}`);
console.log(`Status: ${dashboard.subscription.status}`);
console.log('');
console.log('Usage:');
console.log(` Requests: ${dashboard.usage.requestsThisMonth} / ${dashboard.usage.monthlyQuota}`);
console.log(` Percentage: ${usagePercentage}%`);
console.log(` Remaining: ${dashboard.usage.remaining}`);
console.log('');
console.log('Limits:');
console.log(` Rate Limit: ${dashboard.limits.rateLimit} req/min`);
console.log(` Max API Keys: ${dashboard.limits.maxApiKeys}`);
console.log(` Overage Allowed: ${dashboard.limits.overageAllowed ? 'Yes' : 'No'}`);
console.log(` Price: $${dashboard.limits.price}/month`);
console.log('');
console.log(`API Keys: ${dashboard.apiKeys.active} / ${dashboard.apiKeys.max}`);
console.log('='.repeat(50));
// Alert if quota > 80%
if (parseFloat(usagePercentage) > 80) {
console.warn('⚠️ WARNING: You have used more than 80% of your quota!');
}
return dashboard;
}
buildDashboard();import { ApogeoAPI } from 'apogeoapi';
const client = new ApogeoAPI({ token: 'user_jwt_token' });
async function rotateApiKey(oldKeyId: string) {
// 1. Create new key
const newKey = await client.apiKeys.create({
name: 'Production Key (Rotated)',
expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year
});
console.log('🔑 New API key created:', newKey.key);
console.log('⚠️ Update this in your application NOW!');
// 2. Wait for confirmation
console.log('Press Enter after updating your application...');
await waitForEnter();
// 3. Revoke old key
await client.apiKeys.revoke(oldKeyId);
console.log('✅ Old key revoked');
// 4. Optional: Delete old key after grace period
setTimeout(async () => {
await client.apiKeys.delete(oldKeyId);
console.log('🗑️ Old key deleted');
}, 7 * 24 * 60 * 60 * 1000); // 7 days grace period
return newKey.key;
}
function waitForEnter(): Promise<void> {
return new Promise(resolve => {
process.stdin.once('data', () => resolve());
});
}import { ApogeoAPI } from 'apogeoapi';
const client = new ApogeoAPI({ token: 'user_jwt_token' });
async function upgradeSubscription() {
// 1. Check current tier
const dashboard = await client.account.getDashboard();
console.log('Current tier:', dashboard.subscription.tier);
// 2. Create checkout session
const checkout = await client.billing.createCheckoutSession(
'professional',
'https://myapp.com/upgrade/success',
'https://myapp.com/upgrade/cancel'
);
console.log('Checkout URL:', checkout.url);
console.log('Session ID:', checkout.sessionId);
// 3. Redirect user (in browser)
// window.location.href = checkout.url;
// 4. After successful payment, Stripe webhook will update subscription
// You can then verify:
setTimeout(async () => {
const updatedDashboard = await client.account.getDashboard();
console.log('New tier:', updatedDashboard.subscription.tier);
}, 5000);
}import { ApogeoAPI } from 'apogeoapi';
import express from 'express';
import crypto from 'crypto';
const client = new ApogeoAPI({ token: 'user_jwt_token' });
const app = express();
// 1. Create webhook
async function setupWebhook() {
const webhook = await client.webhooks.create({
url: 'https://myapp.com/webhooks/geo-api',
events: [
'usage.quota_warning',
'usage.quota_exceeded',
'subscription.updated',
'subscription.canceled'
]
});
console.log('Webhook ID:', webhook.id);
console.log('Secret:', webhook.secret);
// Store secret securely
process.env.WEBHOOK_SECRET = webhook.secret;
return webhook;
}
// 2. Validate webhook signature
function validateWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// 3. Handle webhook events
app.post('/webhooks/geo-api', express.raw({ type: 'application/json' }), async (req, res) => {
const signature = req.headers['x-webhook-signature'] as string;
const payload = req.body.toString();
// Validate signature
if (!validateWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature');
}
// Parse event
const event = JSON.parse(payload);
console.log('Webhook event:', event.type);
// Handle different events
switch (event.type) {
case 'usage.quota_warning':
console.log(`⚠️ Quota warning: ${event.data.usagePercentage}%`);
// Send notification to user
break;
case 'usage.quota_exceeded':
console.log('🚫 Quota exceeded!');
// Disable features or notify urgently
break;
case 'subscription.updated':
console.log(`✅ Subscription updated to: ${event.data.tier}`);
// Update user permissions
break;
case 'subscription.canceled':
console.log('❌ Subscription canceled');
// Downgrade user to free tier
break;
}
res.status(200).send('OK');
});
// 4. Test webhook
async function testWebhook(webhookId: string) {
const result = await client.webhooks.test(webhookId);
console.log('Test result:', result.success);
}import { ApogeoAPI, ApogeoAPIError } from 'apogeoapi';
const client = new ApogeoAPI({ apiKey: 'xxx' });
async function robustApiCall<T>(
operation: () => Promise<T>,
retries = 3
): Promise<T> {
for (let i = 0; i < retries; i++) {
try {
return await operation();
} catch (error) {
if (error instanceof ApogeoAPIError) {
// Handle specific error codes
switch (error.statusCode) {
case 401:
console.error('Unauthorized - check API key');
throw error;
case 403:
console.error('Forbidden - quota exceeded or insufficient permissions');
throw error;
case 404:
console.error('Not found');
throw error;
case 429:
console.warn(`Rate limited, retrying after delay (attempt ${i + 1})`);
const retryAfter = error.response?.retryAfter || 1000;
await new Promise(resolve => setTimeout(resolve, retryAfter));
continue;
case 500:
case 502:
case 503:
console.warn(`Server error, retrying (attempt ${i + 1})`);
await new Promise(resolve => setTimeout(resolve, 2000));
continue;
default:
console.error('API Error:', error.message);
throw error;
}
} else {
console.error('Unexpected error:', error);
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
// Usage
const countries = await robustApiCall(() => client.geo.getCountries());// lib/geo-client.ts
import { ApogeoAPI } from 'apogeoapi';
let clientInstance: GeoAPI | null = null;
export function getGeoClient(): GeoAPI {
if (!clientInstance) {
if (!process.env.GEO_API_KEY) {
throw new Error('GEO_API_KEY environment variable is required');
}
clientInstance = new ApogeoAPI({
apiKey: process.env.GEO_API_KEY,
baseURL: process.env.GEO_API_URL,
timeout: 30000,
retries: 3,
debug: process.env.NODE_ENV === 'development'
});
}
return clientInstance;
}
// Usage across your application
import { getGeoClient } from './lib/geo-client';
const client = getGeoClient();
const countries = await client.geo.getCountries();// config/geo-api.config.ts
import { SDKConfig } from 'apogeoapi';
const configs: Record<string, SDKConfig> = {
development: {
apiKey: process.env.DEV_GEO_API_KEY!,
baseURL: 'http://localhost:3000/v1',
timeout: 60000,
retries: 1,
debug: true
},
staging: {
apiKey: process.env.STAGING_GEO_API_KEY!,
baseURL: 'https://staging-api.apogeoapi.com/v1',
timeout: 30000,
retries: 3,
debug: false
},
production: {
apiKey: process.env.PROD_GEO_API_KEY!,
baseURL: 'https://api.apogeoapi.com/v1',
timeout: 30000,
retries: 3,
debug: false
}
};
export function getConfig(): SDKConfig {
const env = process.env.NODE_ENV || 'development';
return configs[env] || configs.development;
}For more examples, see the full documentation.