docs
0
# Extensions and Customization
1
2
ReadMe-specific OpenAPI extensions with validation and configuration management for enhanced API documentation.
3
4
## Capabilities
5
6
### Check Extension Existence
7
8
Determine if a custom specification extension exists within the API definition.
9
10
```typescript { .api }
11
/**
12
* Check if extension exists at API definition root level
13
* @param extension - Extension name to check for
14
* @returns True if extension exists in the API definition
15
*/
16
hasExtension(extension: string): boolean;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
const oas = new Oas(definition);
23
24
// Check for ReadMe extensions
25
if (oas.hasExtension('x-readme')) {
26
console.log("ReadMe extensions configured");
27
}
28
29
// Check for specific extensions
30
if (oas.hasExtension('x-readme.code-samples')) {
31
console.log("Custom code samples defined");
32
}
33
34
// Check for legacy extensions
35
if (oas.hasExtension('x-samples-languages')) {
36
console.log("Legacy samples language configuration found");
37
}
38
```
39
40
### Get Extension Values
41
42
Retrieve custom specification extension values with fallback to defaults.
43
44
```typescript { .api }
45
/**
46
* Get extension value from API definition or operation
47
* @param extension - Extension name or key
48
* @param operation - Optional operation to check for extension (takes precedence)
49
* @returns Extension value or default value
50
*/
51
getExtension(extension: string | keyof Extensions, operation?: Operation): any;
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
// Get API-level extension
58
const codesamples = oas.getExtension('code-samples');
59
console.log("Default code samples:", codesamples);
60
61
// Get operation-level extension (takes precedence)
62
const operation = oas.operation('/users', 'post');
63
const operationSamples = oas.getExtension('code-samples', operation);
64
65
// Get with type safety
66
const languages = oas.getExtension('samples-languages') as string[];
67
const headers = oas.getExtension('headers') as Array<{key: string, value: string}>;
68
69
// Get nested ReadMe extensions
70
const proxyEnabled = oas.getExtension('proxy-enabled'); // From x-readme.proxy-enabled
71
const explorerEnabled = oas.getExtension('explorer-enabled'); // From x-readme.explorer-enabled
72
```
73
74
### Validate Extensions
75
76
Validate extension values against expected schemas and constraints.
77
78
```typescript { .api }
79
/**
80
* Validate a specific extension
81
* @param extension - Extension key to validate
82
* @throws TypeError if extension value is invalid
83
*/
84
validateExtension(extension: keyof Extensions): void;
85
86
/**
87
* Validate all known extensions
88
* @throws TypeError if any extension value is invalid
89
*/
90
validateExtensions(): void;
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
try {
97
// Validate specific extension
98
oas.validateExtension('parameter-ordering');
99
console.log("Parameter ordering is valid");
100
101
// Validate all extensions
102
oas.validateExtensions();
103
console.log("All extensions are valid");
104
105
} catch (error) {
106
console.error("Extension validation failed:", error.message);
107
// Example: "x-readme.parameter-ordering" must contain 6 items comprised of: path, query, body, cookie, form, and header
108
}
109
```
110
111
## Extension Constants and Types
112
113
### Extension Key Constants
114
115
```typescript { .api }
116
/** Extension key constants for type safety */
117
const CODE_SAMPLES = 'code-samples';
118
const EXPLORER_ENABLED = 'explorer-enabled';
119
const HEADERS = 'headers';
120
const METRICS_ENABLED = 'metrics-enabled';
121
const OAUTH_OPTIONS = 'oauth-options';
122
const PARAMETER_ORDERING = 'parameter-ordering';
123
const PROXY_ENABLED = 'proxy-enabled';
124
const SAMPLES_LANGUAGES = 'samples-languages';
125
const SIMPLE_MODE = 'simple-mode';
126
const DISABLE_TAG_SORTING = 'disable-tag-sorting';
127
```
128
129
### Extensions Interface
130
131
```typescript { .api }
132
interface Extensions {
133
/** Custom code samples for operations */
134
'code-samples': Array<{
135
code: string;
136
correspondingExample?: string;
137
install?: string;
138
language: string;
139
name?: string;
140
}>;
141
142
/** Disable automatic tag sorting */
143
'disable-tag-sorting': boolean;
144
145
/** Enable/disable API Explorer */
146
'explorer-enabled': boolean;
147
148
/** Static headers to add to requests */
149
'headers': Array<Record<string, number | string>>;
150
151
/** Enable/disable API metrics collection */
152
'metrics-enabled': boolean;
153
154
/** OAuth2 flow configuration options */
155
'oauth-options': {
156
scopeSeparator?: string;
157
useInsecureClientAuthentication?: boolean;
158
};
159
160
/** Parameter display order in documentation */
161
'parameter-ordering': ('body' | 'cookie' | 'form' | 'header' | 'path' | 'query')[];
162
163
/** Enable/disable CORS proxy */
164
'proxy-enabled': boolean;
165
166
/** Default code sample languages */
167
'samples-languages': string[];
168
169
/** Enable/disable simple mode */
170
'simple-mode': boolean;
171
}
172
```
173
174
### Extension Defaults
175
176
```typescript { .api }
177
/** Default values for all extensions */
178
const extensionDefaults: Extensions = {
179
'code-samples': undefined,
180
'disable-tag-sorting': false,
181
'explorer-enabled': true,
182
'headers': undefined,
183
'metrics-enabled': true,
184
'oauth-options': {},
185
'parameter-ordering': ['path', 'query', 'body', 'cookie', 'form', 'header'],
186
'proxy-enabled': true,
187
'samples-languages': ['shell', 'node', 'ruby', 'php', 'python', 'java', 'csharp'],
188
'simple-mode': true
189
};
190
```
191
192
## Extension Usage Patterns
193
194
### Code Samples Configuration
195
196
```typescript
197
// Check and configure custom code samples
198
const operation = oas.operation('/users', 'post');
199
const customSamples = oas.getExtension('code-samples', operation);
200
201
if (customSamples && Array.isArray(customSamples)) {
202
customSamples.forEach(sample => {
203
console.log(`${sample.language}${sample.name ? ` (${sample.name})` : ''}:`);
204
console.log(sample.code);
205
206
if (sample.install) {
207
console.log(`Installation: ${sample.install}`);
208
}
209
210
if (sample.correspondingExample) {
211
console.log(`Matches example: ${sample.correspondingExample}`);
212
}
213
});
214
}
215
216
// Add custom code samples
217
const definition = oas.getDefinition();
218
if (!definition.paths['/users'].post['x-readme']) {
219
definition.paths['/users'].post['x-readme'] = {};
220
}
221
222
definition.paths['/users'].post['x-readme']['code-samples'] = [
223
{
224
language: 'curl',
225
name: 'Create User with cURL',
226
code: 'curl -X POST https://api.example.com/users -H "Content-Type: application/json" -d \'{"name":"John","email":"john@example.com"}\'',
227
install: 'curl is usually pre-installed'
228
},
229
{
230
language: 'javascript',
231
name: 'Create User with Fetch',
232
code: 'fetch("https://api.example.com/users", {\n method: "POST",\n headers: { "Content-Type": "application/json" },\n body: JSON.stringify({ name: "John", email: "john@example.com" })\n});'
233
}
234
];
235
```
236
237
### API Explorer Configuration
238
239
```typescript
240
// Configure API Explorer behavior
241
const explorerEnabled = oas.getExtension('explorer-enabled');
242
const proxyEnabled = oas.getExtension('proxy-enabled');
243
const metricsEnabled = oas.getExtension('metrics-enabled');
244
245
console.log(`API Explorer: ${explorerEnabled ? 'enabled' : 'disabled'}`);
246
console.log(`CORS Proxy: ${proxyEnabled ? 'enabled' : 'disabled'}`);
247
console.log(`Metrics Collection: ${metricsEnabled ? 'enabled' : 'disabled'}`);
248
249
// Disable API Explorer for sensitive operations
250
const adminOperation = oas.operation('/admin/users', 'delete');
251
const adminExplorerSetting = oas.getExtension('explorer-enabled', adminOperation);
252
253
if (adminExplorerSetting) {
254
console.log("Admin operations allow API Explorer usage");
255
}
256
```
257
258
### Static Headers Configuration
259
260
```typescript
261
// Configure static headers
262
const staticHeaders = oas.getExtension('headers');
263
264
if (staticHeaders && Array.isArray(staticHeaders)) {
265
console.log("Static headers configured:");
266
staticHeaders.forEach(header => {
267
console.log(` ${header.key}: ${header.value}`);
268
});
269
}
270
271
// Add static headers to definition
272
const definition = oas.getDefinition();
273
definition['x-readme'] = {
274
...definition['x-readme'],
275
headers: [
276
{ key: 'X-API-Version', value: '2024-01' },
277
{ key: 'X-Client-ID', value: 'docs-client' },
278
{ key: 'X-Custom-Header', value: 'documentation' }
279
]
280
};
281
```
282
283
### Parameter Ordering Configuration
284
285
```typescript
286
// Get and validate parameter ordering
287
const parameterOrder = oas.getExtension('parameter-ordering');
288
console.log("Parameter display order:", parameterOrder);
289
290
// Validate parameter ordering
291
try {
292
oas.validateExtension('parameter-ordering');
293
} catch (error) {
294
console.error("Invalid parameter ordering:", error.message);
295
}
296
297
// Custom parameter ordering
298
const definition = oas.getDefinition();
299
definition['x-readme'] = {
300
...definition['x-readme'],
301
'parameter-ordering': ['header', 'path', 'query', 'body', 'form', 'cookie']
302
};
303
304
// Re-validate after changes
305
oas.validateExtension('parameter-ordering');
306
```
307
308
### OAuth Configuration
309
310
```typescript
311
// Configure OAuth2 options
312
const oauthOptions = oas.getExtension('oauth-options');
313
314
console.log("OAuth configuration:");
315
console.log(` Scope separator: "${oauthOptions.scopeSeparator || ' '}"`);
316
console.log(` Insecure client auth: ${oauthOptions.useInsecureClientAuthentication || false}`);
317
318
// Custom OAuth configuration
319
const definition = oas.getDefinition();
320
definition['x-readme'] = {
321
...definition['x-readme'],
322
'oauth-options': {
323
scopeSeparator: ',',
324
useInsecureClientAuthentication: false
325
}
326
};
327
```
328
329
## Advanced Extension Patterns
330
331
### Extension Migration
332
333
```typescript
334
// Migrate from legacy extension format to new format
335
function migrateLegacyExtensions(definition: OASDocument): OASDocument {
336
const migrated = JSON.parse(JSON.stringify(definition));
337
338
// Migrate x-samples-languages to x-readme.samples-languages
339
if (migrated['x-samples-languages']) {
340
migrated['x-readme'] = migrated['x-readme'] || {};
341
migrated['x-readme']['samples-languages'] = migrated['x-samples-languages'];
342
delete migrated['x-samples-languages'];
343
}
344
345
// Migrate x-explorer-enabled to x-readme.explorer-enabled
346
if (migrated['x-explorer-enabled'] !== undefined) {
347
migrated['x-readme'] = migrated['x-readme'] || {};
348
migrated['x-readme']['explorer-enabled'] = migrated['x-explorer-enabled'];
349
delete migrated['x-explorer-enabled'];
350
}
351
352
return migrated;
353
}
354
355
const migratedDef = migrateLegacyExtensions(oas.getDefinition());
356
const migratedOas = new Oas(migratedDef);
357
```
358
359
### Extension Inheritance
360
361
```typescript
362
// Check extension inheritance from API to operation level
363
function getEffectiveExtension(oas: Oas, operation: Operation, extension: keyof Extensions): any {
364
// Operation-level takes precedence
365
const operationValue = oas.getExtension(extension, operation);
366
if (operationValue !== undefined) {
367
return operationValue;
368
}
369
370
// Fall back to API-level
371
return oas.getExtension(extension);
372
}
373
374
const operation = oas.operation('/users', 'post');
375
const effectiveLanguages = getEffectiveExtension(oas, operation, 'samples-languages');
376
console.log("Effective sample languages:", effectiveLanguages);
377
```
378
379
### Extension Validation Pipeline
380
381
```typescript
382
// Comprehensive extension validation
383
function validateAllExtensions(oas: Oas): { valid: boolean; errors: string[] } {
384
const errors: string[] = [];
385
386
try {
387
oas.validateExtensions();
388
return { valid: true, errors: [] };
389
} catch (error) {
390
errors.push(error.message);
391
}
392
393
// Additional custom validations
394
const samplesLanguages = oas.getExtension('samples-languages');
395
if (samplesLanguages && !Array.isArray(samplesLanguages)) {
396
errors.push('samples-languages must be an array');
397
}
398
399
const headers = oas.getExtension('headers');
400
if (headers && Array.isArray(headers)) {
401
headers.forEach((header, index) => {
402
if (!header.key || !header.value) {
403
errors.push(`Header at index ${index} missing key or value`);
404
}
405
});
406
}
407
408
return { valid: errors.length === 0, errors };
409
}
410
411
const validation = validateAllExtensions(oas);
412
if (!validation.valid) {
413
console.error("Extension validation failed:");
414
validation.errors.forEach(error => console.error(` ${error}`));
415
}
416
```
417
418
## Integration Examples
419
420
### Documentation Generation
421
422
```typescript
423
// Generate extension-aware documentation
424
function generateDocumentationConfig(oas: Oas) {
425
return {
426
explorerEnabled: oas.getExtension('explorer-enabled'),
427
proxyEnabled: oas.getExtension('proxy-enabled'),
428
metricsEnabled: oas.getExtension('metrics-enabled'),
429
defaultLanguages: oas.getExtension('samples-languages'),
430
parameterOrder: oas.getExtension('parameter-ordering'),
431
staticHeaders: oas.getExtension('headers'),
432
tagSortingDisabled: oas.getExtension('disable-tag-sorting'),
433
simpleMode: oas.getExtension('simple-mode')
434
};
435
}
436
437
const docConfig = generateDocumentationConfig(oas);
438
console.log("Documentation configuration:", docConfig);
439
```
440
441
### Code Generation Integration
442
443
```typescript
444
// Use extensions for code generation customization
445
function generateSDKConfig(oas: Oas) {
446
const languages = oas.getExtension('samples-languages');
447
const headers = oas.getExtension('headers');
448
449
return {
450
targetLanguages: languages,
451
defaultHeaders: headers?.reduce((acc, header) => {
452
acc[header.key] = header.value;
453
return acc;
454
}, {} as Record<string, any>),
455
includeMetrics: oas.getExtension('metrics-enabled'),
456
proxyMode: oas.getExtension('proxy-enabled')
457
};
458
}
459
```