@dinoconfig/js-sdk 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.
package/README.md ADDED
@@ -0,0 +1,743 @@
1
+ # DinoConfig JavaScript SDK
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@dinoconfig/js-sdk.svg)](https://www.npmjs.com/package/@dinoconfig/js-sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@dinoconfig/js-sdk.svg)](https://www.npmjs.com/package/@dinoconfig/js-sdk)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
8
+
9
+ Official JavaScript/TypeScript SDK for the DinoConfig API. This SDK provides a simple, type-safe, and intuitive way to interact with DinoConfig's configuration management system.
10
+
11
+ ## Table of Contents
12
+
13
+ - [Features](#features)
14
+ - [Installation](#installation)
15
+ - [Quick Start](#quick-start)
16
+ - [Configuration Options](#configuration-options)
17
+ - [Authentication](#authentication)
18
+ - [Caching](#caching)
19
+ - [API Reference](#api-reference)
20
+ - [Configs API](#configs-api)
21
+ - [Discovery API](#discovery-api)
22
+ - [Code Generation](#code-generation)
23
+ - [Error Handling](#error-handling)
24
+ - [TypeScript Support](#typescript-support)
25
+ - [Examples](#examples)
26
+ - [Contributing](#contributing)
27
+ - [License](#license)
28
+
29
+ ## Features
30
+
31
+ - **Simple Initialization** - Single factory function following modern SDK patterns
32
+ - **Automatic Authentication** - API key to token exchange handled automatically
33
+ - **Shorthand Path Syntax** - Use `"Brand.Config.Key"` dot notation for concise access
34
+ - **Discovery API** - Discover brands, configs, and schemas dynamically
35
+ - **Full Introspection** - Get complete visibility into all available configurations
36
+ - **Multi-Layer Caching** - In-memory (L1) and persistent storage (L2) caching for improved performance
37
+ - **Code Generation** - Generate TypeScript types from your config schemas
38
+ - **Type-Safe** - Full TypeScript support with comprehensive type definitions
39
+ - **Zero Dependencies** - Uses native `fetch` API, no external HTTP libraries required
40
+ - **Retry Logic** - Built-in exponential backoff for failed requests
41
+ - **Timeout Support** - Configurable request timeouts
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ # npm
47
+ npm install @dinoconfig/js-sdk
48
+
49
+ # yarn
50
+ yarn add @dinoconfig/js-sdk
51
+
52
+ # pnpm
53
+ pnpm add @dinoconfig/js-sdk
54
+ ```
55
+
56
+ ## Quick Start
57
+
58
+ ```typescript
59
+ import { dinoconfigApi } from '@dinoconfig/js-sdk';
60
+
61
+ // Initialize the SDK
62
+ const dinoconfig = await dinoconfigApi({
63
+ apiKey: 'dino_your-api-key-here',
64
+ baseUrl: 'https://api.dinoconfig.com', // optional
65
+ timeout: 10000 // optional
66
+ });
67
+
68
+ // Get entire config
69
+ const config = await dinoconfig.configs.get('MyBrand', 'AppSettings');
70
+ console.log('All values:', config.data.values);
71
+
72
+ // Get single value (shorthand)
73
+ const theme = await dinoconfig.configs.getValue('MyBrand.AppSettings.theme');
74
+ console.log('Theme:', theme.data);
75
+
76
+ // Get single value (full params)
77
+ const response = await dinoconfig.configs.getValue('MyBrand', 'AppSettings', 'theme');
78
+ console.log('Theme:', response.data);
79
+ ```
80
+
81
+ **That's it!** The SDK handles:
82
+ - API key to access token exchange
83
+ - Authorization headers
84
+ - Request formatting and parsing
85
+
86
+ ## Configuration Options
87
+
88
+ | Option | Type | Required | Default | Description |
89
+ |--------|------|----------|---------|-------------|
90
+ | `apiKey` | `string` | **Yes** | - | Your DinoConfig API key |
91
+ | `baseUrl` | `string` | No | `'http://localhost:3000'` | Base URL for the API |
92
+ | `timeout` | `number` | No | `10000` | Request timeout in milliseconds |
93
+ | `cache` | `CacheConfig` | No | `{ enabled: false }` | Cache configuration (see [Caching](#caching)) |
94
+
95
+ ### Example with All Options
96
+
97
+ ```typescript
98
+ const dinoconfig = await dinoconfigApi({
99
+ apiKey: 'dino_abc123def456...',
100
+ baseUrl: 'https://api.dinoconfig.com',
101
+ timeout: 15000,
102
+ cache: {
103
+ enabled: true,
104
+ ttl: 60000,
105
+ storage: 'localStorage',
106
+ }
107
+ });
108
+ ```
109
+
110
+ ## Caching
111
+
112
+ The SDK includes a powerful multi-layer caching system that significantly improves performance by reducing network requests.
113
+
114
+ ### Cache Layers
115
+
116
+ The cache operates on two layers:
117
+
118
+ 1. **L1 - Memory Cache** (Fast, short-lived)
119
+ - In-memory storage for instant access
120
+ - Default TTL: 60 seconds (configurable)
121
+ - Cleared when the application restarts
122
+
123
+ 2. **L2 - Storage Cache** (Persistent, longer-lived)
124
+ - Browser `localStorage` or `IndexedDB` (future)
125
+ - Persists across page reloads
126
+ - Default TTL: 5 minutes (configurable)
127
+
128
+ ### Cache Flow
129
+
130
+ ```
131
+ Request → L1 Memory Cache → L2 Storage Cache → Network API
132
+ (if miss) (if miss) (source of truth)
133
+ ```
134
+
135
+ ### Enabling Caching
136
+
137
+ ```typescript
138
+ const dinoconfig = await dinoconfigApi({
139
+ apiKey: 'dino_your-api-key',
140
+ cache: {
141
+ enabled: true, // Enable caching
142
+ ttl: 60000, // 1 minute TTL for memory cache
143
+ maxSize: 1000, // Maximum cache entries
144
+ storage: 'localStorage', // Use localStorage for persistence
145
+ staleWhileRevalidate: false // Return stale data while refreshing
146
+ }
147
+ });
148
+ ```
149
+
150
+ ### Cache Configuration Options
151
+
152
+ | Option | Type | Default | Description |
153
+ |--------|------|---------|-------------|
154
+ | `enabled` | `boolean` | `false` | Whether caching is enabled |
155
+ | `ttl` | `number` | `60000` | Time-to-live in milliseconds (1 minute) |
156
+ | `maxSize` | `number` | `1000` | Maximum number of entries in memory cache |
157
+ | `storage` | `'memory' \| 'localStorage' \| 'indexedDB'` | `undefined` | Storage backend for L2 cache |
158
+ | `staleWhileRevalidate` | `boolean` | `false` | Return stale data while fetching fresh data |
159
+
160
+ ### Cache-Aware Requests
161
+
162
+ All API methods automatically use the cache when enabled. You can control caching behavior per request:
163
+
164
+ ```typescript
165
+ // Use cache (default behavior when cache is enabled)
166
+ const response = await dinoconfig.configs.getValue(
167
+ 'Brand', 'Config', 'key'
168
+ );
169
+
170
+ // Force refresh (bypass cache)
171
+ const response = await dinoconfig.configs.getValue(
172
+ 'Brand', 'Config', 'key',
173
+ { forceRefresh: true }
174
+ );
175
+
176
+ // Disable cache for this request
177
+ const response = await dinoconfig.configs.getValue(
178
+ 'Brand', 'Config', 'key',
179
+ { cache: false }
180
+ );
181
+ ```
182
+
183
+ ### Cache Management
184
+
185
+ The SDK exposes a `cache` API for manual cache control:
186
+
187
+ ```typescript
188
+ // Get cache statistics
189
+ const stats = dinoconfig.cache.getStats();
190
+ console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}, Hit Rate: ${(stats.hitRate * 100).toFixed(1)}%`);
191
+
192
+ // Clear all cache
193
+ await dinoconfig.cache.clear();
194
+
195
+ // Invalidate cache by pattern (regex)
196
+ await dinoconfig.cache.invalidate('brand:Paysafe:*'); // Clear all Paysafe configs
197
+ await dinoconfig.cache.invalidate('config:.*:.*:featureFlag'); // Clear all feature flags
198
+
199
+ // Prefetch a value into cache
200
+ await dinoconfig.cache.prefetch('key', async () => {
201
+ return await dinoconfig.configs.getValue('Brand', 'Config', 'key');
202
+ });
203
+ ```
204
+
205
+ ### Cache Performance
206
+
207
+ With caching enabled, subsequent requests to the same configuration value are served from cache, providing:
208
+
209
+ - **99%+ faster response times** - Cache hits typically take < 5ms vs 200-500ms for network requests
210
+ - **Reduced API costs** - Fewer network requests
211
+ - **Better offline experience** - Cached values available when network is unavailable
212
+ - **Improved user experience** - Instant configuration value access
213
+
214
+ ### Example: Cache in Action
215
+
216
+ ```typescript
217
+ const dinoconfig = await dinoconfigApi({
218
+ apiKey: 'dino_...',
219
+ cache: {
220
+ enabled: true,
221
+ ttl: 60000,
222
+ storage: 'localStorage',
223
+ }
224
+ });
225
+
226
+ // First request - hits network (~250ms)
227
+ console.time('first');
228
+ const response1 = await dinoconfig.configs.getValue('Brand', 'Config', 'key');
229
+ console.timeEnd('first'); // ~250ms
230
+
231
+ // Second request - served from cache (~1ms)
232
+ console.time('second');
233
+ const response2 = await dinoconfig.configs.getValue('Brand', 'Config', 'key');
234
+ console.timeEnd('second'); // ~1ms ⚡
235
+
236
+ // Check cache performance
237
+ const stats = dinoconfig.cache.getStats();
238
+ console.log(`Cache hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
239
+ ```
240
+
241
+ ### Cache Best Practices
242
+
243
+ 1. **Enable caching for production** - Significantly improves performance
244
+ 2. **Use appropriate TTL** - Balance freshness vs performance (1-5 minutes recommended)
245
+ 3. **Use localStorage for browser apps** - Persists across page reloads
246
+ 4. **Invalidate cache on updates** - Use cache invalidation when you know configs changed
247
+ 5. **Monitor cache hit rates** - Use `getStats()` to track cache effectiveness
248
+
249
+ ## Authentication
250
+
251
+ ### Security Best Practices
252
+
253
+ ```typescript
254
+ // DO: Use environment variables
255
+ const dinoconfig = await dinoconfigApi({
256
+ apiKey: process.env.DINOCONFIG_API_KEY!,
257
+ baseUrl: process.env.DINOCONFIG_BASE_URL
258
+ });
259
+
260
+ // DON'T: Hardcode API keys in source code
261
+ const dinoconfig = await dinoconfigApi({
262
+ apiKey: 'dino_abc123...' // Never do this in production!
263
+ });
264
+ ```
265
+
266
+ ## API Reference
267
+
268
+ ### Configs API
269
+
270
+ The Configs API provides methods to retrieve configuration values.
271
+
272
+ ---
273
+
274
+ #### `configs.get(path, options?)` / `configs.get(brand, config, options?)`
275
+
276
+ Retrieves an entire configuration with all its values.
277
+
278
+ **Signatures:**
279
+ ```typescript
280
+ // Shorthand path
281
+ get(path: string, options?: RequestOptions): Promise<ApiResponse<ConfigData>>
282
+
283
+ // Full parameters
284
+ get(brand: string, config: string, options?: RequestOptions): Promise<ApiResponse<ConfigData>>
285
+ ```
286
+
287
+ **Returns:** `ApiResponse<ConfigData>` containing:
288
+ - `name` - Configuration name
289
+ - `description` - Configuration description
290
+ - `values` - All key-value pairs as `Record<string, unknown>`
291
+ - `version` - Configuration version
292
+ - `keys` - Array of all key names
293
+ - `createdAt` - Creation timestamp
294
+ - `updatedAt` - Last update timestamp
295
+
296
+ **Examples:**
297
+ ```typescript
298
+ // Shorthand path
299
+ const config = await dinoconfig.configs.get('MyBrand.FeatureFlags');
300
+ console.log(config.data.values);
301
+ // { enableDarkMode: true, maxUsers: 100, ... }
302
+
303
+ // Full parameters
304
+ const config = await dinoconfig.configs.get('MyBrand', 'FeatureFlags');
305
+ console.log(`Version: ${config.data.version}`);
306
+ console.log(`Keys: ${config.data.keys.join(', ')}`);
307
+
308
+ // Access specific values
309
+ const { values } = config.data;
310
+ const darkMode = values.enableDarkMode as boolean;
311
+ const maxUsers = values.maxUsers as number;
312
+ ```
313
+
314
+ ---
315
+
316
+ #### `configs.getValue(path, options?)` / `configs.getValue(brand, config, key, options?)`
317
+
318
+ Retrieves a specific configuration value.
319
+
320
+ **Signatures:**
321
+ ```typescript
322
+ // Shorthand path
323
+ getValue(path: string, options?: RequestOptions): Promise<ApiResponse<unknown>>
324
+
325
+ // Full parameters
326
+ getValue(brand: string, config: string, key: string, options?: RequestOptions): Promise<ApiResponse<unknown>>
327
+ ```
328
+
329
+ **Examples:**
330
+ ```typescript
331
+ // Shorthand path (recommended)
332
+ const response = await dinoconfig.configs.getValue('MyBrand.FeatureFlags.enableDarkMode');
333
+ console.log('Dark mode:', response.data); // true
334
+
335
+ // Full parameters
336
+ const response = await dinoconfig.configs.getValue('MyBrand', 'FeatureFlags', 'enableDarkMode');
337
+ console.log('Dark mode:', response.data);
338
+
339
+ // With request options
340
+ const response = await dinoconfig.configs.getValue(
341
+ 'MyBrand.CriticalConfig.databaseUrl',
342
+ { timeout: 30000, retries: 5 }
343
+ );
344
+ ```
345
+
346
+ ---
347
+
348
+ ### Discovery API
349
+
350
+ The Discovery API enables dynamic configuration discovery.
351
+
352
+ ---
353
+
354
+ #### `discovery.listBrands(options?)`
355
+
356
+ Lists all brands accessible by your API key.
357
+
358
+ **Returns:** `ApiResponse<BrandInfo[]>`
359
+
360
+ ```typescript
361
+ const response = await dinoconfig.discovery.listBrands();
362
+ response.data.forEach(brand => {
363
+ console.log(`${brand.name}: ${brand.configCount} configs`);
364
+ });
365
+ ```
366
+
367
+ ---
368
+
369
+ #### `discovery.listConfigs(brandName, options?)`
370
+
371
+ Lists all configurations for a specific brand.
372
+
373
+ **Returns:** `ApiResponse<ConfigInfo[]>`
374
+
375
+ ```typescript
376
+ const response = await dinoconfig.discovery.listConfigs('MyBrand');
377
+ response.data.forEach(config => {
378
+ console.log(`${config.name} (v${config.version}): ${config.keys.length} keys`);
379
+ });
380
+ ```
381
+
382
+ ---
383
+
384
+ #### `discovery.getSchema(brandName, configName, options?)`
385
+
386
+ Gets the schema/structure for a specific configuration.
387
+
388
+ **Returns:** `ApiResponse<ConfigSchema>`
389
+
390
+ ```typescript
391
+ const response = await dinoconfig.discovery.getSchema('MyBrand', 'FeatureFlags');
392
+ Object.entries(response.data.fields).forEach(([name, field]) => {
393
+ console.log(`${name}: ${field.type}${field.required ? ' (required)' : ''}`);
394
+ });
395
+ ```
396
+
397
+ ---
398
+
399
+ #### `discovery.introspect(options?)`
400
+
401
+ Performs full introspection, returning all brands, configs, and values.
402
+
403
+ **Returns:** `ApiResponse<IntrospectionResult>`
404
+
405
+ ```typescript
406
+ const response = await dinoconfig.discovery.introspect();
407
+ const { company, brands } = response.data;
408
+
409
+ console.log(`Company: ${company}`);
410
+ brands.forEach(brand => {
411
+ console.log(`\nBrand: ${brand.name}`);
412
+ brand.configs.forEach(config => {
413
+ console.log(` Config: ${config.name} (${config.keys.length} keys)`);
414
+ });
415
+ });
416
+ ```
417
+
418
+ ---
419
+
420
+ ### Request Options
421
+
422
+ All API methods accept an optional `RequestOptions` object:
423
+
424
+ ```typescript
425
+ interface RequestOptions {
426
+ /** Custom headers for this specific request */
427
+ headers?: Record<string, string>;
428
+ /** Request timeout in milliseconds (overrides default) */
429
+ timeout?: number;
430
+ /** Number of retry attempts for failed requests */
431
+ retries?: number;
432
+ /** Whether to use cache (default: true when cache is enabled) */
433
+ cache?: boolean;
434
+ /** Force refresh from API, bypassing cache */
435
+ forceRefresh?: boolean;
436
+ }
437
+ ```
438
+
439
+ **Example with options:**
440
+ ```typescript
441
+ const response = await dinoconfig.configs.getValue(
442
+ 'MyBrand',
443
+ 'Settings',
444
+ 'apiEndpoint',
445
+ {
446
+ timeout: 5000,
447
+ retries: 3,
448
+ cache: true, // Use cache (default)
449
+ forceRefresh: false, // Don't bypass cache
450
+ headers: {
451
+ 'X-Request-ID': 'unique-request-id'
452
+ }
453
+ }
454
+ );
455
+ ```
456
+
457
+ ## Code Generation
458
+
459
+ Generate TypeScript types from your DinoConfig schemas using the [@dinoconfig/cli](https://www.npmjs.com/package/@dinoconfig/cli) package:
460
+
461
+ ```bash
462
+ # Install CLI
463
+ npm install -g @dinoconfig/cli
464
+
465
+ # Generate types
466
+ npx @dinoconfig/cli codegen --api-key=dino_xxx --output=./src/types/dinoconfig.d.ts
467
+ ```
468
+
469
+ ### Usage with Generated Types
470
+
471
+ ```typescript
472
+ import { dinoconfigApi } from '@dinoconfig/js-sdk';
473
+ import { DinoConfig } from './types/dinoconfig';
474
+
475
+ const dinoconfig = await dinoconfigApi({
476
+ apiKey: process.env.DINOCONFIG_API_KEY!
477
+ });
478
+
479
+ // Full type safety with generics
480
+ const flags = await dinoconfig.configs.get<DinoConfig.MyBrand.FeatureFlags>(
481
+ 'MyBrand',
482
+ 'FeatureFlags'
483
+ );
484
+
485
+ // TypeScript knows the exact types!
486
+ flags.data.values.enableDarkMode; // boolean ✓
487
+ flags.data.values.maxUploadSize; // number ✓
488
+ ```
489
+
490
+ For full documentation, see [@dinoconfig/cli](https://www.npmjs.com/package/@dinoconfig/cli).
491
+ ## Error Handling
492
+
493
+ ```typescript
494
+ try {
495
+ const response = await dinoconfig.configs.getValue('Brand.Config.Key');
496
+ console.log('Value:', response.data);
497
+ } catch (error) {
498
+ if (error instanceof Error) {
499
+ console.error('Error:', error.message);
500
+ }
501
+ }
502
+ ```
503
+
504
+ ### Common Error Scenarios
505
+
506
+ | Status | Meaning | Suggested Action |
507
+ |--------|---------|------------------|
508
+ | 401 | Unauthorized | Check API key validity |
509
+ | 403 | Forbidden | Verify permissions |
510
+ | 404 | Not Found | Check brand/config/key names |
511
+ | 429 | Rate Limited | Implement backoff |
512
+ | 500 | Server Error | Retry with backoff |
513
+
514
+ ## TypeScript Support
515
+
516
+ ### Exported Types
517
+
518
+ ```typescript
519
+ import {
520
+ // Main SDK
521
+ dinoconfigApi,
522
+ DinoConfigInstance,
523
+ DinoConfigSDKConfig,
524
+ ApiResponse,
525
+ RequestOptions,
526
+
527
+ // APIs
528
+ ConfigAPI,
529
+ DiscoveryAPI,
530
+ CacheAPI,
531
+
532
+ // Config types
533
+ ConfigData,
534
+
535
+ // Discovery types
536
+ BrandInfo,
537
+ ConfigInfo,
538
+ ConfigSchema,
539
+ FieldSchema,
540
+ FieldType,
541
+ FieldValidation,
542
+ IntrospectionResult,
543
+ BrandInfoDetail,
544
+ ConfigInfoDetail,
545
+ KeyInfo,
546
+
547
+ // Cache types
548
+ CacheConfig,
549
+ CacheStats
550
+ } from '@dinoconfig/js-sdk';
551
+ ```
552
+
553
+ ### Key Interfaces
554
+
555
+ ```typescript
556
+ /** SDK configuration options */
557
+ interface DinoConfigSDKConfig {
558
+ /** The API key for authentication */
559
+ apiKey: string;
560
+ /** The base URL of the DinoConfig API */
561
+ baseUrl?: string;
562
+ /** Request timeout in milliseconds */
563
+ timeout?: number;
564
+ /** Cache configuration options */
565
+ cache?: Partial<CacheConfig>;
566
+ }
567
+
568
+ /** SDK instance returned by dinoconfigApi() */
569
+ interface DinoConfigInstance {
570
+ /** Configuration API for retrieving config values */
571
+ configs: ConfigAPI;
572
+ /** Discovery API for exploring available brands, configs, and schemas */
573
+ discovery: DiscoveryAPI;
574
+ /** Cache API for managing the cache layer */
575
+ cache: CacheAPI;
576
+ }
577
+
578
+ /** Full config data */
579
+ interface ConfigData {
580
+ readonly name: string;
581
+ readonly description?: string;
582
+ readonly values: Record<string, unknown>;
583
+ readonly version: number;
584
+ readonly keys: readonly string[];
585
+ readonly createdAt: Date;
586
+ readonly updatedAt?: Date;
587
+ }
588
+
589
+ /** API response wrapper */
590
+ interface ApiResponse<T> {
591
+ data: T;
592
+ success: boolean;
593
+ message?: string;
594
+ }
595
+
596
+ /** Request customization options */
597
+ interface RequestOptions {
598
+ /** Custom headers */
599
+ headers?: Record<string, string>;
600
+ /** Request timeout in milliseconds */
601
+ timeout?: number;
602
+ /** Number of retry attempts */
603
+ retries?: number;
604
+ /** Whether to use cache (default: true when cache is enabled) */
605
+ cache?: boolean;
606
+ /** Force refresh from API, bypassing cache */
607
+ forceRefresh?: boolean;
608
+ }
609
+ ```
610
+
611
+ ## Examples
612
+
613
+ ### Basic Usage
614
+
615
+ ```typescript
616
+ import { dinoconfigApi } from '@dinoconfig/js-sdk';
617
+
618
+ const dinoconfig = await dinoconfigApi({
619
+ apiKey: process.env.DINOCONFIG_API_KEY!
620
+ });
621
+
622
+ // Get entire config
623
+ const config = await dinoconfig.configs.get('Brand.Config');
624
+ console.log(config.data.values);
625
+
626
+ // Get single value
627
+ const value = await dinoconfig.configs.getValue('Brand.Config.Key');
628
+ console.log(value.data);
629
+ ```
630
+
631
+ ### Express.js Integration
632
+
633
+ ```typescript
634
+ import express from 'express';
635
+ import { dinoconfigApi, DinoConfigInstance } from '@dinoconfig/js-sdk';
636
+
637
+ let dinoconfig: DinoConfigInstance;
638
+
639
+ async function initApp() {
640
+ dinoconfig = await dinoconfigApi({
641
+ apiKey: process.env.DINOCONFIG_API_KEY!
642
+ });
643
+
644
+ const app = express();
645
+
646
+ app.get('/config/:brand/:config/:key', async (req, res) => {
647
+ try {
648
+ const { brand, config, key } = req.params;
649
+ const response = await dinoconfig.configs.getValue(brand, config, key);
650
+ res.json(response.data);
651
+ } catch (error) {
652
+ res.status(500).json({ error: 'Failed to fetch config' });
653
+ }
654
+ });
655
+
656
+ app.listen(3000);
657
+ }
658
+
659
+ initApp();
660
+ ```
661
+
662
+ ### Next.js API Route
663
+
664
+ ```typescript
665
+ // app/api/config/[...path]/route.ts
666
+ import { dinoconfigApi } from '@dinoconfig/js-sdk';
667
+ import { NextResponse } from 'next/server';
668
+
669
+ export async function GET(
670
+ request: Request,
671
+ { params }: { params: { path: string[] } }
672
+ ) {
673
+ const [brand, config, key] = params.path;
674
+
675
+ const dinoconfig = await dinoconfigApi({
676
+ apiKey: process.env.DINOCONFIG_API_KEY!
677
+ });
678
+
679
+ const response = await dinoconfig.configs.getValue(brand, config, key);
680
+ return NextResponse.json(response.data);
681
+ }
682
+ ```
683
+
684
+ ### With Caching
685
+
686
+ ```typescript
687
+ // Initialize with cache enabled
688
+ const dinoconfig = await dinoconfigApi({
689
+ apiKey: process.env.DINOCONFIG_API_KEY!,
690
+ cache: {
691
+ enabled: true,
692
+ ttl: 60000, // 1 minute
693
+ storage: 'localStorage',
694
+ }
695
+ });
696
+
697
+ // First request - network call (~250ms)
698
+ const response1 = await dinoconfig.configs.getValue('MyBrand', 'Settings', 'apiUrl');
699
+
700
+ // Second request - cached (~1ms) ⚡
701
+ const response2 = await dinoconfig.configs.getValue('MyBrand', 'Settings', 'apiUrl');
702
+
703
+ // Force refresh when needed
704
+ const freshResponse = await dinoconfig.configs.getValue(
705
+ 'MyBrand',
706
+ 'Settings',
707
+ 'apiUrl',
708
+ { forceRefresh: true }
709
+ );
710
+
711
+ // Check cache performance
712
+ const stats = dinoconfig.cache.getStats();
713
+ console.log(`Cache hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
714
+ ```
715
+ ## Requirements
716
+
717
+ - **Node.js** >= 18.0.0 (for native `fetch` support)
718
+ - **TypeScript** >= 5.0.0 (for TypeScript projects)
719
+
720
+ ## Contributing
721
+
722
+ ```bash
723
+ # Clone and install
724
+ git clone https://github.com/dinoconfig/dinoconfig-js-sdk.git
725
+ npm install
726
+
727
+ # Build
728
+ npx nx build dinoconfig-js-sdk
729
+
730
+ # Test
731
+ npx nx test dinoconfig-js-sdk
732
+
733
+ # Lint
734
+ npx nx lint dinoconfig-js-sdk
735
+ ```
736
+
737
+ ## License
738
+
739
+ MIT License - see [LICENSE](LICENSE) for details.
740
+
741
+ ---
742
+
743
+ Made with ❤️ by the DinoConfig Team