apogeoapi 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/EXAMPLES.md +666 -0
  3. package/README.md +532 -0
  4. package/dist/clients/account.client.d.ts +76 -0
  5. package/dist/clients/account.client.d.ts.map +1 -0
  6. package/dist/clients/account.client.js +85 -0
  7. package/dist/clients/account.client.js.map +1 -0
  8. package/dist/clients/api-keys.client.d.ts +90 -0
  9. package/dist/clients/api-keys.client.d.ts.map +1 -0
  10. package/dist/clients/api-keys.client.js +105 -0
  11. package/dist/clients/api-keys.client.js.map +1 -0
  12. package/dist/clients/auth.client.d.ts +63 -0
  13. package/dist/clients/auth.client.d.ts.map +1 -0
  14. package/dist/clients/auth.client.js +86 -0
  15. package/dist/clients/auth.client.js.map +1 -0
  16. package/dist/clients/billing.client.d.ts +79 -0
  17. package/dist/clients/billing.client.d.ts.map +1 -0
  18. package/dist/clients/billing.client.js +101 -0
  19. package/dist/clients/billing.client.js.map +1 -0
  20. package/dist/clients/geo.client.d.ts +105 -0
  21. package/dist/clients/geo.client.d.ts.map +1 -0
  22. package/dist/clients/geo.client.js +149 -0
  23. package/dist/clients/geo.client.js.map +1 -0
  24. package/dist/clients/webhooks.client.d.ts +110 -0
  25. package/dist/clients/webhooks.client.d.ts.map +1 -0
  26. package/dist/clients/webhooks.client.js +129 -0
  27. package/dist/clients/webhooks.client.js.map +1 -0
  28. package/dist/index.d.ts +137 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +157 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/types/index.d.ts +243 -0
  33. package/dist/types/index.d.ts.map +1 -0
  34. package/dist/types/index.js +16 -0
  35. package/dist/types/index.js.map +1 -0
  36. package/dist/utils/http-client.d.ts +43 -0
  37. package/dist/utils/http-client.d.ts.map +1 -0
  38. package/dist/utils/http-client.js +140 -0
  39. package/dist/utils/http-client.js.map +1 -0
  40. package/package.json +44 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to the @geo-api/sdk will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-01-15
9
+
10
+ ### Added
11
+ - Initial release of @geo-api/sdk
12
+ - Full TypeScript support with complete type definitions
13
+ - **AuthClient** - Authentication (register, login, refresh token)
14
+ - **GeoClient** - Geography data (countries, states, cities, search)
15
+ - **AccountClient** - Account management (dashboard, profile, usage stats)
16
+ - **ApiKeysClient** - API keys CRUD operations
17
+ - **BillingClient** - Subscription and billing management
18
+ - **WebhooksClient** - Webhook configuration and logs
19
+ - Auto-retry logic with exponential backoff
20
+ - Rate limit handling with retry-after support
21
+ - Comprehensive error handling with typed errors
22
+ - Request/Response interceptors
23
+ - Configurable timeout and retry settings
24
+ - Debug mode for development
25
+ - Complete JSDoc documentation
26
+ - Extensive README with examples
27
+ - Example usage patterns document
28
+
29
+ ### Features
30
+ - ✅ Automatic token management
31
+ - ✅ Type-safe API with full autocomplete
32
+ - ✅ Zero configuration required (sensible defaults)
33
+ - ✅ Works in Node.js and browser environments
34
+ - ✅ CommonJS and ESM support
35
+
36
+ ### Documentation
37
+ - Complete README with quick start guide
38
+ - API reference for all clients
39
+ - Usage examples for common scenarios
40
+ - Error handling patterns
41
+ - Production best practices
42
+
43
+ ## [Unreleased]
44
+
45
+ ### Planned
46
+ - [ ] WebSocket support for real-time updates
47
+ - [ ] Batch operations support
48
+ - [ ] Response caching layer
49
+ - [ ] Request cancellation support
50
+ - [ ] GraphQL client (if API adds GraphQL)
51
+ - [ ] React hooks package (@geo-api/react)
52
+ - [ ] Vue composables package (@geo-api/vue)
package/EXAMPLES.md ADDED
@@ -0,0 +1,666 @@
1
+ # SDK Usage Examples
2
+
3
+ Complete examples demonstrating how to use the Geo API SDK.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Basic Setup](#basic-setup)
8
+ - [Authentication Flow](#authentication-flow)
9
+ - [Geography Queries](#geography-queries)
10
+ - [Account Management](#account-management)
11
+ - [API Keys Lifecycle](#api-keys-lifecycle)
12
+ - [Subscription Management](#subscription-management)
13
+ - [Webhook Setup](#webhook-setup)
14
+ - [Error Handling Patterns](#error-handling-patterns)
15
+ - [Production Best Practices](#production-best-practices)
16
+
17
+ ---
18
+
19
+ ## Basic Setup
20
+
21
+ ### Node.js / Express Application
22
+
23
+ ```typescript
24
+ import { GeoAPI } from '@geo-api/sdk';
25
+ import express from 'express';
26
+
27
+ const app = express();
28
+ const client = new GeoAPI({
29
+ apiKey: process.env.GEO_API_KEY!,
30
+ baseURL: process.env.GEO_API_URL || 'https://api.yourcompany.com/v1',
31
+ timeout: 30000,
32
+ retries: 3
33
+ });
34
+
35
+ app.get('/countries', async (req, res) => {
36
+ try {
37
+ const countries = await client.geo.getCountries();
38
+ res.json(countries);
39
+ } catch (error) {
40
+ res.status(500).json({ error: error.message });
41
+ }
42
+ });
43
+
44
+ app.listen(3000);
45
+ ```
46
+
47
+ ### Next.js API Route
48
+
49
+ ```typescript
50
+ // pages/api/countries.ts
51
+ import { GeoAPI } from '@geo-api/sdk';
52
+ import type { NextApiRequest, NextApiResponse } from 'next';
53
+
54
+ const client = new GeoAPI({
55
+ apiKey: process.env.GEO_API_KEY!
56
+ });
57
+
58
+ export default async function handler(
59
+ req: NextApiRequest,
60
+ res: NextApiResponse
61
+ ) {
62
+ try {
63
+ const countries = await client.geo.getCountries();
64
+ res.status(200).json(countries);
65
+ } catch (error: any) {
66
+ res.status(error.statusCode || 500).json({
67
+ error: error.message
68
+ });
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### React Hook
74
+
75
+ ```typescript
76
+ // hooks/useGeoAPI.ts
77
+ import { GeoAPI } from '@geo-api/sdk';
78
+ import { useState, useEffect } from 'react';
79
+
80
+ const client = new GeoAPI({
81
+ apiKey: process.env.NEXT_PUBLIC_GEO_API_KEY!
82
+ });
83
+
84
+ export function useCountries() {
85
+ const [countries, setCountries] = useState([]);
86
+ const [loading, setLoading] = useState(true);
87
+ const [error, setError] = useState(null);
88
+
89
+ useEffect(() => {
90
+ client.geo.getCountries()
91
+ .then(setCountries)
92
+ .catch(setError)
93
+ .finally(() => setLoading(false));
94
+ }, []);
95
+
96
+ return { countries, loading, error };
97
+ }
98
+
99
+ // Usage in component
100
+ function CountriesList() {
101
+ const { countries, loading, error } = useCountries();
102
+
103
+ if (loading) return <div>Loading...</div>;
104
+ if (error) return <div>Error: {error.message}</div>;
105
+
106
+ return (
107
+ <ul>
108
+ {countries.map(country => (
109
+ <li key={country.id}>{country.name}</li>
110
+ ))}
111
+ </ul>
112
+ );
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Authentication Flow
119
+
120
+ ### Complete Registration & Login Flow
121
+
122
+ ```typescript
123
+ import { GeoAPI, GeoAPIError } from '@geo-api/sdk';
124
+
125
+ // Initialize without auth (for registration)
126
+ const publicClient = new GeoAPI({
127
+ apiKey: 'public_api_key' // Or omit for public endpoints
128
+ });
129
+
130
+ async function registerAndLogin() {
131
+ try {
132
+ // 1. Register
133
+ const registration = await publicClient.auth.register({
134
+ email: 'user@example.com',
135
+ password: 'SecurePassword123!',
136
+ username: 'johndoe'
137
+ });
138
+
139
+ console.log('User registered:', registration.user);
140
+ console.log('Access token:', registration.access_token);
141
+
142
+ // 2. Create new client with token
143
+ const authenticatedClient = new GeoAPI({
144
+ token: registration.access_token
145
+ });
146
+
147
+ // 3. Verify profile
148
+ const profile = await authenticatedClient.auth.getProfile();
149
+ console.log('Profile:', profile);
150
+
151
+ // 4. Store tokens securely
152
+ localStorage.setItem('access_token', registration.access_token);
153
+ localStorage.setItem('refresh_token', registration.refresh_token);
154
+
155
+ return authenticatedClient;
156
+ } catch (error) {
157
+ if (error instanceof GeoAPIError) {
158
+ if (error.statusCode === 409) {
159
+ console.error('Email already exists');
160
+ } else {
161
+ console.error('Registration failed:', error.message);
162
+ }
163
+ }
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ // Token refresh helper
169
+ async function refreshAuthToken(refreshToken: string) {
170
+ const client = new GeoAPI({ token: 'temp' });
171
+
172
+ try {
173
+ const newTokens = await client.auth.refreshToken({
174
+ refresh_token: refreshToken
175
+ });
176
+
177
+ localStorage.setItem('access_token', newTokens.access_token);
178
+ localStorage.setItem('refresh_token', newTokens.refresh_token);
179
+
180
+ return newTokens.access_token;
181
+ } catch (error) {
182
+ // Refresh failed, redirect to login
183
+ localStorage.clear();
184
+ window.location.href = '/login';
185
+ }
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Geography Queries
192
+
193
+ ### Building a Location Selector
194
+
195
+ ```typescript
196
+ import { GeoAPI, Country, State, City } from '@geo-api/sdk';
197
+
198
+ const client = new GeoAPI({ apiKey: process.env.GEO_API_KEY! });
199
+
200
+ class LocationSelector {
201
+ async getCountries(): Promise<Country[]> {
202
+ return client.geo.getCountries();
203
+ }
204
+
205
+ async getStatesForCountry(countryIso2: string): Promise<State[]> {
206
+ return client.geo.getStates(countryIso2);
207
+ }
208
+
209
+ async getCitiesForState(stateId: number): Promise<City[]> {
210
+ return client.geo.getCities(stateId, 1000); // Get up to 1000 cities
211
+ }
212
+
213
+ async searchLocation(query: string) {
214
+ // Search across all types
215
+ const results = await client.geo.search({
216
+ query,
217
+ limit: 10
218
+ });
219
+
220
+ return {
221
+ countries: results.data.filter(r => r.type === 'country'),
222
+ states: results.data.filter(r => r.type === 'state'),
223
+ cities: results.data.filter(r => r.type === 'city')
224
+ };
225
+ }
226
+ }
227
+
228
+ // Usage
229
+ const selector = new LocationSelector();
230
+
231
+ // Cascade dropdowns
232
+ const countries = await selector.getCountries();
233
+ const states = await selector.getStatesForCountry('US');
234
+ const cities = await selector.getCitiesForState(1234);
235
+
236
+ // Search as user types
237
+ const results = await selector.searchLocation('new');
238
+ console.log(results); // { countries: [...], states: [...], cities: [...] }
239
+ ```
240
+
241
+ ### Caching Geography Data
242
+
243
+ ```typescript
244
+ import { GeoAPI, Country } from '@geo-api/sdk';
245
+
246
+ class CachedGeoClient {
247
+ private cache = new Map<string, any>();
248
+ private ttl = 24 * 60 * 60 * 1000; // 24 hours
249
+
250
+ constructor(private client: GeoAPI) {}
251
+
252
+ async getCountries(): Promise<Country[]> {
253
+ const cacheKey = 'countries';
254
+ const cached = this.cache.get(cacheKey);
255
+
256
+ if (cached && Date.now() - cached.timestamp < this.ttl) {
257
+ return cached.data;
258
+ }
259
+
260
+ const data = await this.client.geo.getCountries();
261
+ this.cache.set(cacheKey, { data, timestamp: Date.now() });
262
+ return data;
263
+ }
264
+
265
+ async getCountryByIso(iso2: string): Promise<Country> {
266
+ const cacheKey = `country:${iso2}`;
267
+ const cached = this.cache.get(cacheKey);
268
+
269
+ if (cached && Date.now() - cached.timestamp < this.ttl) {
270
+ return cached.data;
271
+ }
272
+
273
+ const data = await this.client.geo.getCountryByIso(iso2);
274
+ this.cache.set(cacheKey, { data, timestamp: Date.now() });
275
+ return data;
276
+ }
277
+
278
+ clearCache() {
279
+ this.cache.clear();
280
+ }
281
+ }
282
+
283
+ // Usage
284
+ const client = new GeoAPI({ apiKey: 'xxx' });
285
+ const cachedClient = new CachedGeoClient(client);
286
+
287
+ const countries = await cachedClient.getCountries(); // Fetches from API
288
+ const countriesAgain = await cachedClient.getCountries(); // Returns from cache
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Account Management
294
+
295
+ ### Usage Monitoring Dashboard
296
+
297
+ ```typescript
298
+ import { GeoAPI } from '@geo-api/sdk';
299
+
300
+ const client = new GeoAPI({ apiKey: 'xxx' });
301
+
302
+ async function buildDashboard() {
303
+ const dashboard = await client.account.getDashboard();
304
+
305
+ const usagePercentage = (
306
+ (dashboard.usage.requestsThisMonth / dashboard.usage.monthlyQuota) * 100
307
+ ).toFixed(2);
308
+
309
+ console.log('='.repeat(50));
310
+ console.log('ACCOUNT DASHBOARD');
311
+ console.log('='.repeat(50));
312
+ console.log(`User: ${dashboard.user.username} (${dashboard.user.email})`);
313
+ console.log(`Plan: ${dashboard.subscription.tier.toUpperCase()}`);
314
+ console.log(`Status: ${dashboard.subscription.status}`);
315
+ console.log('');
316
+ console.log('Usage:');
317
+ console.log(` Requests: ${dashboard.usage.requestsThisMonth} / ${dashboard.usage.monthlyQuota}`);
318
+ console.log(` Percentage: ${usagePercentage}%`);
319
+ console.log(` Remaining: ${dashboard.usage.remaining}`);
320
+ console.log('');
321
+ console.log('Limits:');
322
+ console.log(` Rate Limit: ${dashboard.limits.rateLimit} req/min`);
323
+ console.log(` Max API Keys: ${dashboard.limits.maxApiKeys}`);
324
+ console.log(` Overage Allowed: ${dashboard.limits.overageAllowed ? 'Yes' : 'No'}`);
325
+ console.log(` Price: $${dashboard.limits.price}/month`);
326
+ console.log('');
327
+ console.log(`API Keys: ${dashboard.apiKeys.active} / ${dashboard.apiKeys.max}`);
328
+ console.log('='.repeat(50));
329
+
330
+ // Alert if quota > 80%
331
+ if (parseFloat(usagePercentage) > 80) {
332
+ console.warn('⚠️ WARNING: You have used more than 80% of your quota!');
333
+ }
334
+
335
+ return dashboard;
336
+ }
337
+
338
+ buildDashboard();
339
+ ```
340
+
341
+ ---
342
+
343
+ ## API Keys Lifecycle
344
+
345
+ ### Rotating API Keys
346
+
347
+ ```typescript
348
+ import { GeoAPI } from '@geo-api/sdk';
349
+
350
+ const client = new GeoAPI({ token: 'user_jwt_token' });
351
+
352
+ async function rotateApiKey(oldKeyId: string) {
353
+ // 1. Create new key
354
+ const newKey = await client.apiKeys.create({
355
+ name: 'Production Key (Rotated)',
356
+ expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year
357
+ });
358
+
359
+ console.log('🔑 New API key created:', newKey.key);
360
+ console.log('⚠️ Update this in your application NOW!');
361
+
362
+ // 2. Wait for confirmation
363
+ console.log('Press Enter after updating your application...');
364
+ await waitForEnter();
365
+
366
+ // 3. Revoke old key
367
+ await client.apiKeys.revoke(oldKeyId);
368
+ console.log('✅ Old key revoked');
369
+
370
+ // 4. Optional: Delete old key after grace period
371
+ setTimeout(async () => {
372
+ await client.apiKeys.delete(oldKeyId);
373
+ console.log('🗑️ Old key deleted');
374
+ }, 7 * 24 * 60 * 60 * 1000); // 7 days grace period
375
+
376
+ return newKey.key;
377
+ }
378
+
379
+ function waitForEnter(): Promise<void> {
380
+ return new Promise(resolve => {
381
+ process.stdin.once('data', () => resolve());
382
+ });
383
+ }
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Subscription Management
389
+
390
+ ### Complete Upgrade Flow
391
+
392
+ ```typescript
393
+ import { GeoAPI } from '@geo-api/sdk';
394
+
395
+ const client = new GeoAPI({ token: 'user_jwt_token' });
396
+
397
+ async function upgradeSubscription() {
398
+ // 1. Check current tier
399
+ const dashboard = await client.account.getDashboard();
400
+ console.log('Current tier:', dashboard.subscription.tier);
401
+
402
+ // 2. Create checkout session
403
+ const checkout = await client.billing.createCheckoutSession(
404
+ 'professional',
405
+ 'https://myapp.com/upgrade/success',
406
+ 'https://myapp.com/upgrade/cancel'
407
+ );
408
+
409
+ console.log('Checkout URL:', checkout.url);
410
+ console.log('Session ID:', checkout.sessionId);
411
+
412
+ // 3. Redirect user (in browser)
413
+ // window.location.href = checkout.url;
414
+
415
+ // 4. After successful payment, Stripe webhook will update subscription
416
+ // You can then verify:
417
+ setTimeout(async () => {
418
+ const updatedDashboard = await client.account.getDashboard();
419
+ console.log('New tier:', updatedDashboard.subscription.tier);
420
+ }, 5000);
421
+ }
422
+ ```
423
+
424
+ ---
425
+
426
+ ## Webhook Setup
427
+
428
+ ### Complete Webhook Implementation
429
+
430
+ ```typescript
431
+ import { GeoAPI } from '@geo-api/sdk';
432
+ import express from 'express';
433
+ import crypto from 'crypto';
434
+
435
+ const client = new GeoAPI({ token: 'user_jwt_token' });
436
+ const app = express();
437
+
438
+ // 1. Create webhook
439
+ async function setupWebhook() {
440
+ const webhook = await client.webhooks.create({
441
+ url: 'https://myapp.com/webhooks/geo-api',
442
+ events: [
443
+ 'usage.quota_warning',
444
+ 'usage.quota_exceeded',
445
+ 'subscription.updated',
446
+ 'subscription.canceled'
447
+ ]
448
+ });
449
+
450
+ console.log('Webhook ID:', webhook.id);
451
+ console.log('Secret:', webhook.secret);
452
+
453
+ // Store secret securely
454
+ process.env.WEBHOOK_SECRET = webhook.secret;
455
+
456
+ return webhook;
457
+ }
458
+
459
+ // 2. Validate webhook signature
460
+ function validateWebhookSignature(
461
+ payload: string,
462
+ signature: string,
463
+ secret: string
464
+ ): boolean {
465
+ const expectedSignature = crypto
466
+ .createHmac('sha256', secret)
467
+ .update(payload)
468
+ .digest('hex');
469
+
470
+ return crypto.timingSafeEqual(
471
+ Buffer.from(signature),
472
+ Buffer.from(expectedSignature)
473
+ );
474
+ }
475
+
476
+ // 3. Handle webhook events
477
+ app.post('/webhooks/geo-api', express.raw({ type: 'application/json' }), async (req, res) => {
478
+ const signature = req.headers['x-webhook-signature'] as string;
479
+ const payload = req.body.toString();
480
+
481
+ // Validate signature
482
+ if (!validateWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
483
+ return res.status(401).send('Invalid signature');
484
+ }
485
+
486
+ // Parse event
487
+ const event = JSON.parse(payload);
488
+
489
+ console.log('Webhook event:', event.type);
490
+
491
+ // Handle different events
492
+ switch (event.type) {
493
+ case 'usage.quota_warning':
494
+ console.log(`⚠️ Quota warning: ${event.data.usagePercentage}%`);
495
+ // Send notification to user
496
+ break;
497
+
498
+ case 'usage.quota_exceeded':
499
+ console.log('🚫 Quota exceeded!');
500
+ // Disable features or notify urgently
501
+ break;
502
+
503
+ case 'subscription.updated':
504
+ console.log(`✅ Subscription updated to: ${event.data.tier}`);
505
+ // Update user permissions
506
+ break;
507
+
508
+ case 'subscription.canceled':
509
+ console.log('❌ Subscription canceled');
510
+ // Downgrade user to free tier
511
+ break;
512
+ }
513
+
514
+ res.status(200).send('OK');
515
+ });
516
+
517
+ // 4. Test webhook
518
+ async function testWebhook(webhookId: string) {
519
+ const result = await client.webhooks.test(webhookId);
520
+ console.log('Test result:', result.success);
521
+ }
522
+ ```
523
+
524
+ ---
525
+
526
+ ## Error Handling Patterns
527
+
528
+ ### Comprehensive Error Handler
529
+
530
+ ```typescript
531
+ import { GeoAPI, GeoAPIError } from '@geo-api/sdk';
532
+
533
+ const client = new GeoAPI({ apiKey: 'xxx' });
534
+
535
+ async function robustApiCall<T>(
536
+ operation: () => Promise<T>,
537
+ retries = 3
538
+ ): Promise<T> {
539
+ for (let i = 0; i < retries; i++) {
540
+ try {
541
+ return await operation();
542
+ } catch (error) {
543
+ if (error instanceof GeoAPIError) {
544
+ // Handle specific error codes
545
+ switch (error.statusCode) {
546
+ case 401:
547
+ console.error('Unauthorized - check API key');
548
+ throw error;
549
+
550
+ case 403:
551
+ console.error('Forbidden - quota exceeded or insufficient permissions');
552
+ throw error;
553
+
554
+ case 404:
555
+ console.error('Not found');
556
+ throw error;
557
+
558
+ case 429:
559
+ console.warn(`Rate limited, retrying after delay (attempt ${i + 1})`);
560
+ const retryAfter = error.response?.retryAfter || 1000;
561
+ await new Promise(resolve => setTimeout(resolve, retryAfter));
562
+ continue;
563
+
564
+ case 500:
565
+ case 502:
566
+ case 503:
567
+ console.warn(`Server error, retrying (attempt ${i + 1})`);
568
+ await new Promise(resolve => setTimeout(resolve, 2000));
569
+ continue;
570
+
571
+ default:
572
+ console.error('API Error:', error.message);
573
+ throw error;
574
+ }
575
+ } else {
576
+ console.error('Unexpected error:', error);
577
+ throw error;
578
+ }
579
+ }
580
+ }
581
+
582
+ throw new Error('Max retries exceeded');
583
+ }
584
+
585
+ // Usage
586
+ const countries = await robustApiCall(() => client.geo.getCountries());
587
+ ```
588
+
589
+ ---
590
+
591
+ ## Production Best Practices
592
+
593
+ ### Singleton Client Pattern
594
+
595
+ ```typescript
596
+ // lib/geo-client.ts
597
+ import { GeoAPI } from '@geo-api/sdk';
598
+
599
+ let clientInstance: GeoAPI | null = null;
600
+
601
+ export function getGeoClient(): GeoAPI {
602
+ if (!clientInstance) {
603
+ if (!process.env.GEO_API_KEY) {
604
+ throw new Error('GEO_API_KEY environment variable is required');
605
+ }
606
+
607
+ clientInstance = new GeoAPI({
608
+ apiKey: process.env.GEO_API_KEY,
609
+ baseURL: process.env.GEO_API_URL,
610
+ timeout: 30000,
611
+ retries: 3,
612
+ debug: process.env.NODE_ENV === 'development'
613
+ });
614
+ }
615
+
616
+ return clientInstance;
617
+ }
618
+
619
+ // Usage across your application
620
+ import { getGeoClient } from './lib/geo-client';
621
+
622
+ const client = getGeoClient();
623
+ const countries = await client.geo.getCountries();
624
+ ```
625
+
626
+ ### Environment-Specific Configuration
627
+
628
+ ```typescript
629
+ // config/geo-api.config.ts
630
+ import { SDKConfig } from '@geo-api/sdk';
631
+
632
+ const configs: Record<string, SDKConfig> = {
633
+ development: {
634
+ apiKey: process.env.DEV_GEO_API_KEY!,
635
+ baseURL: 'http://localhost:3000/v1',
636
+ timeout: 60000,
637
+ retries: 1,
638
+ debug: true
639
+ },
640
+
641
+ staging: {
642
+ apiKey: process.env.STAGING_GEO_API_KEY!,
643
+ baseURL: 'https://staging-api.yourcompany.com/v1',
644
+ timeout: 30000,
645
+ retries: 3,
646
+ debug: false
647
+ },
648
+
649
+ production: {
650
+ apiKey: process.env.PROD_GEO_API_KEY!,
651
+ baseURL: 'https://api.yourcompany.com/v1',
652
+ timeout: 30000,
653
+ retries: 3,
654
+ debug: false
655
+ }
656
+ };
657
+
658
+ export function getConfig(): SDKConfig {
659
+ const env = process.env.NODE_ENV || 'development';
660
+ return configs[env] || configs.development;
661
+ }
662
+ ```
663
+
664
+ ---
665
+
666
+ **For more examples, see the [full documentation](https://api.yourcompany.com/docs).**