0
# Extension System
1
2
Annotations for adding custom extensions and examples to Swagger specifications. The extension system allows you to add vendor-specific or custom metadata to any Swagger element, while example annotations provide sample data for better API documentation and testing.
3
4
## Capabilities
5
6
### @Extension Annotation
7
8
Defines custom extensions for Swagger specification elements. Extensions allow you to add vendor-specific or custom metadata that extends beyond the standard Swagger specification. All extension names are automatically prefixed with "x-" to follow OpenAPI conventions.
9
10
```java { .api }
11
/**
12
* Defines custom extensions for Swagger specification
13
* Target: ANNOTATION_TYPE
14
* Retention: RUNTIME
15
*/
16
@Target(ElementType.ANNOTATION_TYPE)
17
@Retention(RetentionPolicy.RUNTIME)
18
@interface Extension {
19
/**
20
* Name of the extension
21
* Will be prefixed with "x-" automatically in the generated specification
22
* Use descriptive names that indicate the extension's purpose
23
*/
24
String name() default "";
25
26
/**
27
* Array of name/value pairs for the extension
28
* Contains the actual extension data as key-value properties
29
* REQUIRED ATTRIBUTE
30
*/
31
ExtensionProperty[] properties();
32
}
33
```
34
35
**Usage Examples:**
36
37
```java
38
// Simple extension with single property
39
@ApiOperation(
40
value = "Get user profile",
41
extensions = {
42
@Extension(
43
name = "code-samples",
44
properties = {
45
@ExtensionProperty(name = "javascript", value = "fetch('/users/123')")
46
}
47
)
48
}
49
)
50
@GET
51
@Path("/{id}")
52
public User getUser(@PathParam("id") Long id) {
53
// implementation
54
}
55
56
// Multiple extensions on model property
57
public class Product {
58
@ApiModelProperty(
59
value = "Product price in cents",
60
example = "1299",
61
extensions = {
62
@Extension(
63
name = "validation",
64
properties = {
65
@ExtensionProperty(name = "min", value = "0"),
66
@ExtensionProperty(name = "max", value = "999999"),
67
@ExtensionProperty(name = "currency", value = "USD")
68
}
69
),
70
@Extension(
71
name = "display",
72
properties = {
73
@ExtensionProperty(name = "format", value = "currency"),
74
@ExtensionProperty(name = "symbol", value = "$")
75
}
76
)
77
}
78
)
79
private Integer price;
80
}
81
82
// Extension with JSON parsing
83
@ApiOperation(
84
value = "Create order",
85
extensions = {
86
@Extension(
87
name = "workflow",
88
properties = {
89
@ExtensionProperty(
90
name = "steps",
91
value = "[\"validate\", \"process\", \"fulfill\", \"notify\"]",
92
parseValue = true // Parse as JSON array
93
)
94
}
95
)
96
}
97
)
98
@POST
99
public Order createOrder(CreateOrderRequest request) {
100
// implementation
101
}
102
103
// Vendor-specific extensions
104
@ApiModelProperty(
105
value = "Customer ID",
106
extensions = {
107
@Extension(
108
name = "amazon-integration",
109
properties = {
110
@ExtensionProperty(name = "marketplace-id", value = "A1PA6795UKMFR9"),
111
@ExtensionProperty(name = "seller-id", value = "A2EXAMPLE123")
112
}
113
),
114
@Extension(
115
name = "analytics",
116
properties = {
117
@ExtensionProperty(name = "track-conversions", value = "true"),
118
@ExtensionProperty(name = "segment", value = "premium-customers")
119
}
120
)
121
}
122
)
123
private String customerId;
124
```
125
126
### @ExtensionProperty Annotation
127
128
Defines name/value pairs within extensions. Each property represents a key-value pair of metadata that gets added to the extension object in the generated Swagger specification.
129
130
```java { .api }
131
/**
132
* Defines name/value pairs within extensions
133
* Target: ANNOTATION_TYPE
134
* Retention: RUNTIME
135
*/
136
@Target(ElementType.ANNOTATION_TYPE)
137
@Retention(RetentionPolicy.RUNTIME)
138
@interface ExtensionProperty {
139
/**
140
* Property name within the extension
141
* Used as the key in the extension object
142
* REQUIRED ATTRIBUTE
143
*/
144
String name();
145
146
/**
147
* Property value within the extension
148
* Can be any string value or JSON when parseValue is true
149
* REQUIRED ATTRIBUTE
150
*/
151
String value();
152
153
/**
154
* Whether to parse the value as JSON/YAML
155
* When true, the value string is parsed as structured data
156
* When false (default), the value is treated as a literal string
157
*/
158
boolean parseValue() default false;
159
}
160
```
161
162
**Usage Examples:**
163
164
```java
165
// String properties
166
@Extension(
167
name = "security",
168
properties = {
169
@ExtensionProperty(name = "classification", value = "confidential"),
170
@ExtensionProperty(name = "data-owner", value = "finance-team"),
171
@ExtensionProperty(name = "retention-period", value = "7-years")
172
}
173
)
174
175
// Numeric and boolean properties
176
@Extension(
177
name = "rate-limiting",
178
properties = {
179
@ExtensionProperty(name = "requests-per-minute", value = "100"),
180
@ExtensionProperty(name = "burst-limit", value = "20"),
181
@ExtensionProperty(name = "enabled", value = "true")
182
}
183
)
184
185
// JSON object properties
186
@Extension(
187
name = "client-config",
188
properties = {
189
@ExtensionProperty(
190
name = "timeout-settings",
191
value = "{\"connect\": 5000, \"read\": 30000, \"total\": 35000}",
192
parseValue = true
193
),
194
@ExtensionProperty(
195
name = "retry-policy",
196
value = "{\"maxAttempts\": 3, \"backoffMs\": 1000, \"exponential\": true}",
197
parseValue = true
198
)
199
}
200
)
201
202
// Array properties
203
@Extension(
204
name = "allowed-origins",
205
properties = {
206
@ExtensionProperty(
207
name = "domains",
208
value = "[\"https://app.example.com\", \"https://admin.example.com\"]",
209
parseValue = true
210
)
211
}
212
)
213
214
// Complex nested structure
215
@Extension(
216
name = "monitoring",
217
properties = {
218
@ExtensionProperty(
219
name = "metrics",
220
value = "{" +
221
"\"enabled\": true, " +
222
"\"collectors\": [\"prometheus\", \"datadog\"], " +
223
"\"sampling\": {\"rate\": 0.1, \"max-traces\": 1000}" +
224
"}",
225
parseValue = true
226
)
227
}
228
)
229
```
230
231
### @Example Annotation
232
233
Container for multiple example properties, used to provide sample data for request/response bodies. Examples help API consumers understand the expected data format and structure.
234
235
```java { .api }
236
/**
237
* Container for multiple example properties
238
* Target: ANNOTATION_TYPE
239
* Retention: RUNTIME
240
*/
241
@Target(ElementType.ANNOTATION_TYPE)
242
@Retention(RetentionPolicy.RUNTIME)
243
@interface Example {
244
/**
245
* Array of example properties
246
* Each property defines an example for a specific media type
247
* REQUIRED ATTRIBUTE
248
*/
249
ExampleProperty[] value();
250
}
251
```
252
253
**Usage Examples:**
254
255
```java
256
// Examples for request body
257
@ApiOperation(value = "Create user account")
258
@ApiParam(
259
value = "User registration data",
260
examples = @Example({
261
@ExampleProperty(
262
mediaType = "application/json",
263
value = "{" +
264
"\"username\": \"johndoe\"," +
265
"\"email\": \"john@example.com\"," +
266
"\"firstName\": \"John\"," +
267
"\"lastName\": \"Doe\"" +
268
"}"
269
),
270
@ExampleProperty(
271
mediaType = "application/xml",
272
value = "<user>" +
273
"<username>johndoe</username>" +
274
"<email>john@example.com</email>" +
275
"<firstName>John</firstName>" +
276
"<lastName>Doe</lastName>" +
277
"</user>"
278
)
279
})
280
)
281
@POST
282
public User createUser(UserRegistrationRequest request) {
283
// implementation
284
}
285
286
// Response examples
287
@ApiResponse(
288
code = 200,
289
message = "Product details retrieved successfully",
290
response = Product.class,
291
examples = @Example({
292
@ExampleProperty(
293
mediaType = "application/json",
294
value = "{" +
295
"\"id\": 12345," +
296
"\"name\": \"Wireless Headphones\"," +
297
"\"description\": \"High-quality wireless headphones with noise cancellation\"," +
298
"\"price\": 199.99," +
299
"\"category\": \"Electronics\"," +
300
"\"inStock\": true," +
301
"\"tags\": [\"audio\", \"wireless\", \"noise-cancelling\"]" +
302
"}"
303
)
304
})
305
)
306
@GET
307
@Path("/{id}")
308
public Product getProduct(@PathParam("id") Long id) {
309
// implementation
310
}
311
312
// Multiple examples for different scenarios
313
@ApiParam(
314
value = "Order data",
315
examples = @Example({
316
// Minimal order example
317
@ExampleProperty(
318
mediaType = "application/json",
319
value = "{" +
320
"\"customerId\": \"cust_123\"," +
321
"\"items\": [{\"productId\": \"prod_456\", \"quantity\": 1}]" +
322
"}"
323
),
324
// Complete order example with all fields
325
@ExampleProperty(
326
mediaType = "application/json",
327
value = "{" +
328
"\"customerId\": \"cust_123\"," +
329
"\"items\": [" +
330
"{\"productId\": \"prod_456\", \"quantity\": 2, \"price\": 29.99}," +
331
"{\"productId\": \"prod_789\", \"quantity\": 1, \"price\": 15.50}" +
332
"]," +
333
"\"shippingAddress\": {" +
334
"\"street\": \"123 Main St\"," +
335
"\"city\": \"Anytown\"," +
336
"\"zipCode\": \"12345\"," +
337
"\"country\": \"US\"" +
338
"}," +
339
"\"paymentMethod\": \"credit_card\"," +
340
"\"notes\": \"Please leave at front door\"" +
341
"}"
342
)
343
})
344
)
345
@POST
346
@Path("/orders")
347
public Order createOrder(CreateOrderRequest request) {
348
// implementation
349
}
350
```
351
352
### @ExampleProperty Annotation
353
354
Defines media type and example value pairs. Each property associates a specific example with a media type, allowing different examples for different content types.
355
356
```java { .api }
357
/**
358
* Defines media type and example value pairs
359
* Target: ANNOTATION_TYPE
360
* Retention: RUNTIME
361
*/
362
@Target(ElementType.ANNOTATION_TYPE)
363
@Retention(RetentionPolicy.RUNTIME)
364
@interface ExampleProperty {
365
/**
366
* Media type for this example
367
* Specifies the content type this example applies to
368
* Common values: "application/json", "application/xml", "text/plain"
369
*/
370
String mediaType() default "";
371
372
/**
373
* Example value for the specified media type
374
* Should be a valid representation in the specified media type format
375
* REQUIRED ATTRIBUTE
376
*/
377
String value();
378
}
379
```
380
381
**Usage Examples:**
382
383
```java
384
// JSON examples
385
@ExampleProperty(
386
mediaType = "application/json",
387
value = "{\"message\": \"Hello, World!\"}"
388
)
389
390
// XML examples
391
@ExampleProperty(
392
mediaType = "application/xml",
393
value = "<message>Hello, World!</message>"
394
)
395
396
// Plain text examples
397
@ExampleProperty(
398
mediaType = "text/plain",
399
value = "Hello, World!"
400
)
401
402
// CSV examples
403
@ExampleProperty(
404
mediaType = "text/csv",
405
value = "id,name,email\n1,John Doe,john@example.com\n2,Jane Smith,jane@example.com"
406
)
407
408
// Complex JSON structures
409
@ExampleProperty(
410
mediaType = "application/json",
411
value = "{" +
412
"\"user\": {" +
413
"\"id\": 123," +
414
"\"profile\": {" +
415
"\"name\": \"John Doe\"," +
416
"\"preferences\": {" +
417
"\"theme\": \"dark\"," +
418
"\"notifications\": true," +
419
"\"languages\": [\"en\", \"es\"]" +
420
"}" +
421
"}" +
422
"}," +
423
"\"metadata\": {" +
424
"\"created\": \"2023-01-15T10:30:00Z\"," +
425
"\"lastUpdated\": \"2023-12-01T14:22:33Z\"" +
426
"}" +
427
"}"
428
)
429
430
// Error response examples
431
@ExampleProperty(
432
mediaType = "application/json",
433
value = "{" +
434
"\"error\": {" +
435
"\"code\": \"VALIDATION_FAILED\"," +
436
"\"message\": \"The request contains invalid data\"," +
437
"\"details\": [" +
438
"{\"field\": \"email\", \"message\": \"Invalid email format\"}," +
439
"{\"field\": \"age\", \"message\": \"Age must be between 13 and 120\"}" +
440
"]" +
441
"}" +
442
"}"
443
)
444
```
445
446
### Complete Integration Example
447
448
Here's a comprehensive example showing how all extension and example annotations work together:
449
450
```java
451
@Api(tags = "orders")
452
@Path("/orders")
453
public class OrderController {
454
455
@ApiOperation(
456
value = "Create a new order",
457
notes = "Creates a new order with the provided details. Supports both simple and complex order structures.",
458
response = Order.class,
459
code = 201,
460
extensions = {
461
@Extension(
462
name = "workflow",
463
properties = {
464
@ExtensionProperty(name = "type", value = "async"),
465
@ExtensionProperty(name = "estimated-duration", value = "2-5 seconds"),
466
@ExtensionProperty(
467
name = "steps",
468
value = "[\"validate\", \"inventory-check\", \"payment-process\", \"fulfill\"]",
469
parseValue = true
470
)
471
}
472
),
473
@Extension(
474
name = "rate-limiting",
475
properties = {
476
@ExtensionProperty(name = "per-minute", value = "10"),
477
@ExtensionProperty(name = "per-hour", value = "100"),
478
@ExtensionProperty(name = "burst-allowance", value = "5")
479
}
480
)
481
}
482
)
483
@ApiResponses({
484
@ApiResponse(
485
code = 201,
486
message = "Order created successfully",
487
response = Order.class,
488
examples = @Example({
489
@ExampleProperty(
490
mediaType = "application/json",
491
value = "{" +
492
"\"id\": \"ord_1234567890\"," +
493
"\"status\": \"pending\"," +
494
"\"customerId\": \"cust_123\"," +
495
"\"total\": 75.48," +
496
"\"currency\": \"USD\"," +
497
"\"createdAt\": \"2023-12-01T10:30:00Z\"," +
498
"\"items\": [" +
499
"{\"productId\": \"prod_456\", \"quantity\": 2, \"unitPrice\": 29.99}," +
500
"{\"productId\": \"prod_789\", \"quantity\": 1, \"unitPrice\": 15.50}" +
501
"]" +
502
"}"
503
)
504
})
505
),
506
@ApiResponse(
507
code = 400,
508
message = "Invalid order data",
509
examples = @Example({
510
@ExampleProperty(
511
mediaType = "application/json",
512
value = "{" +
513
"\"error\": {" +
514
"\"code\": \"INVALID_ORDER_DATA\"," +
515
"\"message\": \"The order contains invalid information\"," +
516
"\"details\": [" +
517
"{\"field\": \"customerId\", \"message\": \"Customer ID is required\"}," +
518
"{\"field\": \"items[0].quantity\", \"message\": \"Quantity must be positive\"}" +
519
"]" +
520
"}" +
521
"}"
522
)
523
})
524
)
525
})
526
@POST
527
public Response createOrder(
528
@ApiParam(
529
value = "Order creation request",
530
required = true,
531
examples = @Example({
532
// Simple order example
533
@ExampleProperty(
534
mediaType = "application/json",
535
value = "{" +
536
"\"customerId\": \"cust_123\"," +
537
"\"items\": [" +
538
"{\"productId\": \"prod_456\", \"quantity\": 1}" +
539
"]" +
540
"}"
541
),
542
// Complex order example
543
@ExampleProperty(
544
mediaType = "application/json",
545
value = "{" +
546
"\"customerId\": \"cust_123\"," +
547
"\"items\": [" +
548
"{\"productId\": \"prod_456\", \"quantity\": 2}," +
549
"{\"productId\": \"prod_789\", \"quantity\": 1}" +
550
"]," +
551
"\"shippingAddress\": {" +
552
"\"name\": \"John Doe\"," +
553
"\"street\": \"123 Main St\"," +
554
"\"city\": \"Springfield\"," +
555
"\"state\": \"IL\"," +
556
"\"zipCode\": \"62701\"," +
557
"\"country\": \"US\"" +
558
"}," +
559
"\"billingAddress\": {" +
560
"\"name\": \"John Doe\"," +
561
"\"street\": \"123 Main St\"," +
562
"\"city\": \"Springfield\"," +
563
"\"state\": \"IL\"," +
564
"\"zipCode\": \"62701\"," +
565
"\"country\": \"US\"" +
566
"}," +
567
"\"paymentMethod\": {" +
568
"\"type\": \"credit_card\"," +
569
"\"cardToken\": \"tok_1234567890\"" +
570
"}," +
571
"\"specialInstructions\": \"Please call before delivery\"," +
572
"\"giftMessage\": \"Happy Birthday!\"," +
573
"\"expedited\": false" +
574
"}"
575
),
576
// XML format example
577
@ExampleProperty(
578
mediaType = "application/xml",
579
value = "<order>" +
580
"<customerId>cust_123</customerId>" +
581
"<items>" +
582
"<item>" +
583
"<productId>prod_456</productId>" +
584
"<quantity>1</quantity>" +
585
"</item>" +
586
"</items>" +
587
"</order>"
588
)
589
})
590
)
591
CreateOrderRequest orderRequest
592
) {
593
// implementation
594
return Response.status(201).build();
595
}
596
}
597
598
// Model with extensions and examples
599
@ApiModel(description = "Order creation request")
600
public class CreateOrderRequest {
601
602
@ApiModelProperty(
603
value = "Customer identifier",
604
required = true,
605
example = "cust_123",
606
extensions = {
607
@Extension(
608
name = "validation",
609
properties = {
610
@ExtensionProperty(name = "pattern", value = "^cust_[a-zA-Z0-9]{3,20}$"),
611
@ExtensionProperty(name = "required", value = "true")
612
}
613
),
614
@Extension(
615
name = "database",
616
properties = {
617
@ExtensionProperty(name = "table", value = "customers"),
618
@ExtensionProperty(name = "foreign-key", value = "id")
619
}
620
)
621
}
622
)
623
private String customerId;
624
625
@ApiModelProperty(
626
value = "List of items to order",
627
required = true,
628
extensions = {
629
@Extension(
630
name = "constraints",
631
properties = {
632
@ExtensionProperty(name = "min-items", value = "1"),
633
@ExtensionProperty(name = "max-items", value = "50")
634
}
635
)
636
}
637
)
638
private List<OrderItem> items;
639
640
// getters and setters
641
}
642
```
643
644
### Best Practices
645
646
1. **Use meaningful extension names** - Choose descriptive names that clearly indicate the extension's purpose
647
2. **Follow naming conventions** - Use lowercase with hyphens for consistency (e.g., "rate-limiting", "custom-validation")
648
3. **Leverage parseValue for structured data** - Use JSON parsing for complex configuration or nested objects
649
4. **Provide examples for all media types** - Include examples for each supported content type (JSON, XML, etc.)
650
5. **Keep examples realistic** - Use representative data that reflects actual API usage
651
6. **Document extension purposes** - Include comments explaining what each extension accomplishes
652
7. **Validate extension values** - Ensure extension values are valid for their intended use
653
8. **Use extensions sparingly** - Only add extensions when they provide genuine value to API consumers
654
9. **Maintain consistent structure** - Use similar patterns across related extensions
655
10. **Include error examples** - Provide examples of error responses to help with error handling
656
11. **Version your extensions** - Consider versioning when extensions change over time
657
12. **Test generated specifications** - Verify that extensions appear correctly in the generated Swagger/OpenAPI spec