OpenAPI & Swagger
Giriş — Neden Bu Konu Önemli?
API dökümantasyonu, bir REST API'nin en kritik bileşenlerinden biridir. Geliştiricilerin API'nizi nasıl kullanacağını anlaması için açık, güncel ve interaktif dökümantasyon şarttır. Dökümantasyonu olmayan bir API, kullanım kılavuzu olmayan bir cihaz gibidir — teknik olarak çalışır ama kimse nasıl kullanacağını bilemez.
OpenAPI Specification (eski adıyla Swagger Specification) API dökümantasyonu için endüstri standardıdır. springdoc-openapi kütüphanesi, Spring Boot uygulamalarınız için otomatik olarak OpenAPI dökümantasyonu oluşturur — kodunuz değiştikçe dökümantasyon da güncellenir.
Gerçek Hayat Analojisi: Bir restoranın menüsünü düşünün. Menüde yemeklerin adı, açıklaması, fiyatı, alerjen bilgisi, porsiyon boyutu yazılıdır. Müşteri menüye bakarak ne sipariş edebileceğini anlar. OpenAPI, API'nizin "dijital menüsü"dür — endpoint'lerin listesi, parametreleri, request/response formatları ve hata kodları. Swagger UI ise bu menüyü interaktif olarak sunan "tablet menü"dür — müşteri (geliştirici) doğrudan menüden sipariş verebilir (API çağrısı yapabilir).
OpenAPI vs Swagger — Fark Ne?
Swagger: Orijinal olarak SmartBear tarafından geliştirilen API dökümantasyon aracı. 2015'te Open API Initiative'e devredildi.
OpenAPI Specification (OAS): Swagger'ın evrimleşmiş, açık standart hali. Güncel versiyon: OAS 3.1
Swagger UI: OpenAPI spesifikasyonunu görselleştiren web arayüzü
springdoc-openapi: Spring Boot için OpenAPI dökümantasyonu oluşturan kütüphane
Kısacası: OpenAPI = standart, Swagger UI = görselleştirme aracı, springdoc-openapi = Spring Boot entegrasyonu.
springdoc-openapi Kurulumu
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.0</version>
</dependency>Bu tek dependency ile:
Swagger UI →
http://localhost:8080/swagger-ui.htmlOpenAPI JSON →
http://localhost:8080/v3/api-docsOpenAPI YAML →
http://localhost:8080/v3/api-docs.yaml
Herhangi bir ek yapılandırma gerekmeden, tüm @RestController endpoint'leriniz otomatik olarak taranır ve dökümante edilir. Sıfır konfigürasyon ile çalışan dökümantasyon!
application.yml Yapılandırması
springdoc:
api-docs:
path: /v3/api-docs
swagger-ui:
path: /swagger-ui.html
operationsSorter: method # metoda göre sırala (GET, POST, PUT...)
tagsSorter: alpha # tag'leri alfabetik sırala
tryItOutEnabled: true # "Try it out" varsayılan açık
filter: true # arama filtresi aktif
docExpansion: none # endpoint'ler kapalı başlasın
default-produces-media-type: application/json
default-consumes-media-type: application/json
show-actuator: false # Actuator endpoint'lerini göstermeGenel API Bilgisi Yapılandırması
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("E-Ticaret API")
.version("2.0.0")
.description("""
E-Ticaret platformu REST API dökümantasyonu.
## Kimlik Doğrulama
Korunan endpoint'ler JWT Bearer token gerektirir.
Token almak için `/api/auth/login` endpoint'ini kullanın.
## Rate Limiting
API dakikada 100 istek ile sınırlıdır.
""")
.contact(new Contact()
.name("API Destek")
.email("api@example.com")
.url("https://example.com"))
.license(new License()
.name("MIT")
.url("https://opensource.org/licenses/MIT")))
.externalDocs(new ExternalDocumentation()
.description("Detaylı Geliştirici Rehberi")
.url("https://docs.example.com"))
.servers(List.of(
new Server().url("http://localhost:8080").description("Development"),
new Server().url("https://api.example.com").description("Production")
));
}
}@Tag — Endpoint Gruplama
Endpoint'leri mantıksal gruplara ayırmak için @Tag kullanılır:
@RestController
@RequestMapping("/api/users")
@Tag(name = "User", description = "Kullanıcı yönetimi işlemleri")
public class UserController { ... }
@RestController
@RequestMapping("/api/products")
@Tag(name = "Product", description = "Ürün kataloğu işlemleri")
public class ProductController { ... }
@RestController
@RequestMapping("/api/orders")
@Tag(name = "Order", description = "Sipariş yönetimi işlemleri")
public class OrderController { ... }Swagger UI'da endpoint'ler bu tag'lere göre gruplanır — büyük API'lerde navigasyonu kolaylaştırır.
@Operation — Endpoint Açıklaması
@Operation annotation'ı, her endpoint'i detaylı şekilde açıklamak için kullanılır:
@Operation(
summary = "Kullanıcı getir",
description = """
Belirtilen ID'ye sahip kullanıcıyı getirir.
Kullanıcı bulunamazsa 404 döner.
Silinen kullanıcılar bu endpoint'ten dönmez.
"""
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Kullanıcı başarıyla bulundu",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = UserDto.class)
)
),
@ApiResponse(
responseCode = "404",
description = "Kullanıcı bulunamadı",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ProblemDetail.class)
)
),
@ApiResponse(
responseCode = "401",
description = "Authentication gerekli",
content = @Content
)
})
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUser(
@Parameter(description = "Kullanıcı ID", example = "42", required = true)
@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}POST Endpoint Örneği
@Operation(
summary = "Yeni kullanıcı oluştur",
description = "E-posta adresi benzersiz olmalıdır. Başarılı oluşturmada 201 döner."
)
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "Kullanıcı başarıyla oluşturuldu",
headers = @Header(
name = "Location",
description = "Oluşturulan kaynağın URI'si",
schema = @Schema(type = "string", example = "/api/users/42")
)
),
@ApiResponse(responseCode = "400", description = "Geçersiz request body"),
@ApiResponse(responseCode = "409", description = "E-posta adresi zaten kullanılıyor"),
@ApiResponse(responseCode = "422", description = "Validation hatası")
})
@PostMapping
public ResponseEntity<UserDto> createUser(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "Kullanıcı bilgileri",
required = true,
content = @Content(schema = @Schema(implementation = CreateUserDto.class))
)
@Valid @RequestBody CreateUserDto dto) {
UserDto created = userService.create(dto);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(created.id()).toUri();
return ResponseEntity.created(location).body(created);
}@Schema — Model Açıklaması
DTO sınıflarınızı detaylı şekilde dökümante eder:
@Schema(description = "Kullanıcı oluşturma/güncelleme isteği")
public record CreateUserDto(
@Schema(description = "Kullanıcı adı", example = "Ali",
minLength = 2, maxLength = 50, requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Size(min = 2, max = 50)
String firstName,
@Schema(description = "Kullanıcı soyadı", example = "Yılmaz",
minLength = 2, maxLength = 50, requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Size(min = 2, max = 50)
String lastName,
@Schema(description = "E-posta adresi", example = "ali@example.com",
format = "email", requiredMode = Schema.RequiredMode.REQUIRED)
@Email @NotBlank
String email,
@Schema(description = "Doğum tarihi", example = "1990-05-15", format = "date")
LocalDate birthDate,
@Schema(description = "Kullanıcı rolü",
allowableValues = {"ADMIN", "USER", "MODERATOR"},
defaultValue = "USER")
String role
) {}Yanıt DTO Örneği
@Schema(description = "Kullanıcı bilgileri yanıtı")
public record UserDto(
@Schema(description = "Benzersiz kullanıcı ID", example = "42", accessMode = Schema.AccessMode.READ_ONLY)
Long id,
@Schema(description = "Tam ad", example = "Ali Yılmaz")
String fullName,
@Schema(description = "E-posta adresi", example = "ali@example.com")
String email,
@Schema(description = "Hesap durumu", example = "true")
boolean active,
@Schema(description = "Kayıt tarihi", example = "2024-01-15T10:30:00")
LocalDateTime createdAt
) {}@Parameter — Parametre Açıklaması
@GetMapping
@Operation(summary = "Kullanıcı arama")
public Page<UserDto> searchUsers(
@Parameter(description = "Arama terimi (isim veya email)", example = "ali")
@RequestParam(required = false) String query,
@Parameter(description = "Sayfa numarası (0-tabanlı)", example = "0")
@RequestParam(defaultValue = "0") int page,
@Parameter(description = "Sayfa boyutu",
example = "20",
schema = @Schema(minimum = "1", maximum = "100"))
@RequestParam(defaultValue = "20") int size,
@Parameter(description = "Sıralama alanı ve yönü", example = "name,asc")
@RequestParam(defaultValue = "id,asc") String sort,
@Parameter(description = "Aktiflik durumu filtresi",
schema = @Schema(allowableValues = {"true", "false", "all"}))
@RequestParam(defaultValue = "all") String active) {
return userService.search(query, PageRequest.of(page, size));
}Security Şeması — JWT ile Swagger UI
JWT veya API Key ile korunan endpoint'lerin Swagger UI'da test edilebilmesi için güvenlik şeması tanımlanır:
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("API").version("1.0"))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("JWT token — format: Bearer {token}")
));
}Swagger UI'da "Authorize" butonu belirir. Token'ı girdikten sonra tüm isteklere Authorization: Bearer <token> header'ı otomatik eklenir.
Endpoint Bazlı Güvenlik
// Bu endpoint authentication gerektirmez
@Operation(summary = "Public endpoint", security = {})
@GetMapping("/public/info")
public Map<String, String> getPublicInfo() { ... }
// Bu endpoint sadece ADMIN rolü gerektirir
@Operation(summary = "Admin endpoint",
security = @SecurityRequirement(name = "bearerAuth"))
@GetMapping("/admin/stats")
@PreAuthorize("hasRole('ADMIN')")
public AdminStats getStats() { ... }Gruplandırma — Birden Fazla API Doc
Büyük uygulamalarda API'yi gruplara ayırabilirsiniz:
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public-api")
.displayName("Public API")
.pathsToMatch("/api/v1/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin-api")
.displayName("Admin API")
.pathsToMatch("/api/admin/**")
.build();
}
@Bean
public GroupedOpenApi internalApi() {
return GroupedOpenApi.builder()
.group("internal-api")
.displayName("Internal API")
.pathsToMatch("/internal/**")
.build();
}Swagger UI'da dropdown ile gruplar arasında geçiş yapabilirsiniz.
YAML Export ve Kod Üretimi
OpenAPI spesifikasyonu YAML olarak dışa aktarılabilir ve bu dosya kullanılarak istemci SDK'ları otomatik üretilebilir:
# YAML dökümantasyonu indir
curl http://localhost:8080/v3/api-docs.yaml -o api-docs.yaml
# OpenAPI Generator ile TypeScript istemcisi oluştur
npx @openapitools/openapi-generator-cli generate \
-i api-docs.yaml \
-g typescript-axios \
-o ./generated-client
# Java istemcisi oluştur
npx @openapitools/openapi-generator-cli generate \
-i api-docs.yaml \
-g java \
--additional-properties=library=resttemplate \
-o ./java-client
# Python istemcisi
npx @openapitools/openapi-generator-cli generate \
-i api-docs.yaml \
-g python \
-o ./python-clientBu, "Contract-First" yaklaşımının temelidir: OpenAPI spec'i yazın, sonra hem server hem client kodunu otomatik üretin.
Üretim Ortamında Güvenlik
# Production'da Swagger UI'ı kapatın!
springdoc:
swagger-ui:
enabled: false # Swagger UI kapalı
api-docs:
enabled: false # OpenAPI endpoint kapalı
# Veya profil bazlı
spring:
profiles:
active: prod
---
spring:
config:
activate:
on-profile: dev
springdoc:
swagger-ui:
enabled: true
---
spring:
config:
activate:
on-profile: prod
springdoc:
swagger-ui:
enabled: false⚠️ Üretim ortamında Swagger UI'ı asla açık bırakmayın! API yapınızı, endpoint'lerinizi ve parametre detaylarını herkesin görmesi güvenlik riski oluşturur. Swagger UI sadece development ve staging ortamlarında aktif olmalıdır.
Validation ile Entegrasyon
Bean Validation annotation'ları (@NotBlank, @Size, @Email vb.) springdoc-openapi tarafından otomatik olarak tanınır ve OpenAPI spec'ine yansıtılır:
public record CreateProductDto(
@NotBlank
@Size(min = 2, max = 100)
String name, // → required: true, minLength: 2, maxLength: 100
@NotNull
@Positive
BigDecimal price, // → required: true, minimum: 0 (exclusive)
@Min(0) @Max(10000)
int stockQuantity, // → minimum: 0, maximum: 10000
@NotBlank
@Pattern(regexp = "^[A-Z]{2}-\\d{4}$")
String sku, // → pattern: "^[A-Z]{2}-\\d{4}$"
@Email
String contactEmail, // → format: "email"
@Past
LocalDate releaseDate, // → geçmiş tarih olmalı
@URL
String imageUrl // → format: "url"
) {}Bu annotation'lar hem runtime validation hem de API dökümantasyonu olarak çift görev görür — DRY prensibi!
Özel Endpoint'leri Gizleme
Bazı endpoint'lerin dökümantasyonda görünmesini istemeyebilirsiniz:
// Endpoint'i tamamen gizle
@Operation(hidden = true)
@GetMapping("/internal/health-check")
public String healthCheck() { return "OK"; }
// Veya @Hidden annotation'ı ile
@Hidden
@RestController
@RequestMapping("/internal")
public class InternalController { ... }Belirli Yolları Hariç Tutma
springdoc:
paths-to-exclude:
- /internal/**
- /actuator/**
packages-to-scan: com.example.controllerÖrnek Değerler ve Request Body
Swagger UI'da "Try it out" butonuna basıldığında gösterilecek örnek değerler:
@PostMapping("/orders")
@Operation(summary = "Yeni sipariş oluştur")
public ResponseEntity<OrderDto> createOrder(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "Sipariş bilgileri",
content = @Content(
schema = @Schema(implementation = CreateOrderDto.class),
examples = {
@ExampleObject(
name = "Basit sipariş",
summary = "Tek ürünlü sipariş",
value = """
{
"customerId": 42,
"items": [
{"productId": 1, "quantity": 2}
],
"shippingAddress": "İstanbul, Türkiye"
}
"""
),
@ExampleObject(
name = "Çoklu ürün",
summary = "Birden fazla ürünlü sipariş",
value = """
{
"customerId": 42,
"items": [
{"productId": 1, "quantity": 2},
{"productId": 5, "quantity": 1},
{"productId": 12, "quantity": 3}
],
"shippingAddress": "Ankara, Türkiye",
"note": "Hediye paketi olsun"
}
"""
)
}
)
)
@Valid @RequestBody CreateOrderDto dto) {
// ...
}Swagger UI'da kullanıcı dropdown'dan örnek seçip doğrudan test edebilir — API keşfedilebilirliği artırır.
Özet
OpenAPI API dökümantasyonu için endüstri standardıdır — springdoc-openapi Spring Boot entegrasyonu sağlar
Sıfır konfigürasyonla tüm endpoint'ler otomatik dökümante edilir — Swagger UI + JSON/YAML
@Operation endpoint açıklaması, @Schema model açıklaması, @Parameter parametre açıklaması için kullanılır
@ApiResponse ile her status code için açıklama ve örnek yanıt tanımlanır
Security şeması ile JWT token'ı Swagger UI'dan doğrudan test edilebilir
GroupedOpenApi ile büyük API'ler mantıksal gruplara ayrılabilir
OpenAPI spec'inden otomatik istemci SDK üretimi yapılabilir (TypeScript, Java, Python...)
Üretim ortamında Swagger UI'ı kapatın — güvenlik riski oluşturur
AI Asistan
Sorularını yanıtlamaya hazır