Spring Boot/OpenAPI/Swagger với springdoc-openapi — auto-generate API doc
~22 phútREST API với Spring MVCMiễn phí

OpenAPI/Swagger với springdoc-openapi — auto-generate API doc

OpenAPI 3.x là chuẩn industry mô tả REST API. Bài này bóc springdoc-openapi (Boot 3 native), auto-detect controller annotation → spec, customize qua @Operation/@Schema, security scheme, group multi-API, và pattern API-first vs code-first.

REST API không có doc = không usable. Frontend không biết endpoint nào, field gì, status code nào. Trước 2014, doc viết tay trong Confluence — luôn outdated, sai khác code. OpenAPI Specification (trước: Swagger 2.0) ra đời 2014 chuẩn hoá: format JSON/YAML mô tả API + tool sinh client + UI test.

Bài này bóc springdoc-openapi — lib auto-generate OpenAPI spec từ Spring controller annotation. Đặt 1 dependency, get full doc UI tại /swagger-ui.html. Customize qua @Operation, @Schema, security scheme. Pattern thực tế cho TaskFlow capstone.

1. OpenAPI là gì

OpenAPI Specification (OAS) — chuẩn JSON/YAML mô tả REST API. Phiên bản hiện tại 3.1 (2021).

Example minimal spec:

openapi: 3.1.0
info:
  title: Order API
  version: 1.0.0
paths:
  /orders/{id}:
    get:
      operationId: getOrder
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Order found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDto'
        '404':
          description: Order not found
components:
  schemas:
    OrderDto:
      type: object
      properties:
        id: { type: integer, format: int64 }
        customer: { type: string }
        total: { type: number }

Spec mô tả toàn bộ contract: endpoint, method, parameter, body, response status, schema.

1.1 Tool ecosystem

OpenAPI spec là fuel cho ecosystem tool:

ToolUse
Swagger UIRender spec thành interactive HTML — try-it-out
OpenAPI GeneratorSinh client SDK 50+ ngôn ngữ (TypeScript, Python, Kotlin, ...)
PostmanImport spec → auto-create collection
SpectralLint spec — catch design issue
Stoplight StudioVisual editor cho spec
API Mocking (Prism, Mockoon)Run spec as mock server

Lợi ích: API-first workflow — design spec trước, frontend + backend implement song song dựa vào spec. Không phải đợi backend xong mới dev frontend.

2. springdoc-openapi — Boot 3 native

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.6.0</version>
</dependency>

Add 1 dependency, no config required. Boot autoconfig:

  • Scan controller @RestController + annotation MVC.
  • Generate spec at /v3/api-docs (JSON) and /v3/api-docs.yaml.
  • Render Swagger UI at /swagger-ui.html.

Run app, mở browser http://localhost:8080/swagger-ui.html:

[Swagger UI]
├── Order API (1.0.0)
│   ├── GET /api/v1/orders                  List orders
│   │   ├── Parameters: page, size, status
│   │   └── Responses: 200, 400
│   ├── GET /api/v1/orders/{id}             Get order by ID
│   ├── POST /api/v1/orders                 Create order
│   ├── PUT /api/v1/orders/{id}             Update order
│   └── DELETE /api/v1/orders/{id}          Delete order
└── Schemas
    ├── OrderRequest
    ├── OrderDto
    └── ProblemDetail

Click endpoint → expand detail → "Try it out" → execute trực tiếp từ UI. Cực kỳ tiện debug + onboard dev mới.

2.1 Auto-detection

springdoc đọc:

  • Controller: @RestController, @RequestMapping, @GetMapping/@PostMapping/...
  • Parameter: @PathVariable, @RequestParam, @RequestBody, @RequestHeader.
  • Validation: @NotNull, @Size, @Min, @Max, @Pattern → schema constraint.
  • Response: return type + ResponseEntity<T>.
  • Status code: @ResponseStatus annotation.
  • Exception: @ControllerAdvice @ExceptionHandler → error response examples.

Đa phần auto đủ — bạn không cần annotate gì thêm.

3. Customize spec — annotation

Khi auto không đủ, dùng OpenAPI annotation từ io.swagger.v3.oas.annotations:

3.1 @Operation — describe endpoint

@Operation(
    summary = "Get order by ID",
    description = "Retrieves order details by its unique ID. Returns 404 if order not found.",
    tags = {"Orders"}
)
@GetMapping("/orders/{id}")
public OrderDto get(@PathVariable Long id) { ... }

summary xuất hiện trong UI list. description xuất hiện khi expand. tags group endpoint thành section trong UI.

3.2 @Parameter — describe parameter

@GetMapping("/orders")
public List<OrderDto> list(
    @Parameter(description = "Filter by status", example = "ACTIVE")
    @RequestParam(required = false) OrderStatus status,

    @Parameter(description = "Page number (0-indexed)", example = "0")
    @RequestParam(defaultValue = "0") int page,

    @Parameter(description = "Page size (1-100)", example = "20")
    @RequestParam(defaultValue = "20") int size
) { ... }

example show trong UI — user thấy ngay value valid.

3.3 @Schema — describe DTO

@Schema(description = "Order request payload")
public record OrderRequest(

    @Schema(description = "Customer name", example = "Alice", minLength = 2, maxLength = 100)
    @NotBlank
    String customer,

    @Schema(description = "Order total in VND", example = "99.99", minimum = "0.01")
    @NotNull @Positive
    BigDecimal total,

    @Schema(description = "Delivery date (ISO format)", example = "2026-04-20")
    @NotNull @Future
    LocalDate deliveryDate,

    @Schema(description = "Order items", minItems = 1, maxItems = 100)
    @NotEmpty @Size(max = 100)
    @Valid
    List<OrderItem> items
) {}

example rất quan trọng — user copy trong UI làm starting point.

3.4 @ApiResponses — describe response codes

@Operation(summary = "Create order")
@ApiResponses({
    @ApiResponse(responseCode = "201", description = "Order created",
                 content = @Content(schema = @Schema(implementation = OrderDto.class))),
    @ApiResponse(responseCode = "400", description = "Validation failed",
                 content = @Content(schema = @Schema(implementation = ProblemDetail.class))),
    @ApiResponse(responseCode = "409", description = "Duplicate order"),
    @ApiResponse(responseCode = "500", description = "Internal error")
})
@PostMapping("/orders")
public ResponseEntity<OrderDto> create(@Valid @RequestBody OrderRequest req) { ... }

springdoc auto-detect 200/400 từ @RestControllerAdvice — nhưng explicit @ApiResponses cho documentation control.

4. OpenAPI metadata global

Customize info, server, contact:

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("OLHub TaskFlow API")
                .version("v1.0")
                .description("REST API for TaskFlow project management")
                .contact(new Contact()
                    .name("OLHub Engineering")
                    .email("[email protected]")
                    .url("https://docs.olhub.org"))
                .license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0")))
            .servers(List.of(
                new Server().url("https://api.olhub.org").description("Production"),
                new Server().url("https://staging.olhub.org").description("Staging"),
                new Server().url("http://localhost:8080").description("Local dev")
            ))
            .externalDocs(new ExternalDocumentation()
                .description("Full API documentation")
                .url("https://docs.olhub.org"));
    }
}

Hoặc qua property:

springdoc:
  api-docs:
    path: /v3/api-docs
    enabled: true
  swagger-ui:
    path: /swagger-ui.html
    operationsSorter: method
    tagsSorter: alpha
    tryItOutEnabled: true
    filter: true
    deepLinking: true
  show-actuator: false                # exclude actuator endpoints from docs
  packages-to-scan: com.olhub.api      # chi scan package nay
  paths-to-match: /api/**              # chi match URL pattern

5. Security scheme

App có JWT auth — UI cần "Authorize" button để inject token:

@Configuration
@SecurityScheme(
    name = "bearerAuth",
    type = SecuritySchemeType.HTTP,
    scheme = "bearer",
    bearerFormat = "JWT",
    in = SecuritySchemeIn.HEADER
)
public class OpenApiConfig {
    // ...
}

// Apply per controller
@SecurityRequirement(name = "bearerAuth")
@RestController
@RequestMapping("/api/orders")
public class OrderController { ... }

// Hoac global
@Bean
public OpenAPI customOpenAPI() {
    return new OpenAPI()
        .addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
        .components(new Components()
            .addSecuritySchemes("bearerAuth",
                new SecurityScheme()
                    .type(SecurityScheme.Type.HTTP)
                    .scheme("bearer")
                    .bearerFormat("JWT")));
}

UI hiện "Authorize" button. User paste JWT token → mọi request "Try it out" tự include Authorization: Bearer <token> header.

OAuth2 / OIDC scheme tương tự:

@SecurityScheme(
    name = "oauth2",
    type = SecuritySchemeType.OAUTH2,
    flows = @OAuthFlows(
        authorizationCode = @OAuthFlow(
            authorizationUrl = "https://auth.olhub.org/oauth2/authorize",
            tokenUrl = "https://auth.olhub.org/oauth2/token",
            scopes = {
                @OAuthScope(name = "read", description = "Read access"),
                @OAuthScope(name = "write", description = "Write access")
            }
        )
    )
)

6. Group multiple API

Big app có nhiều API context — public, admin, internal — group spec:

@Configuration
public class GroupedOpenApiConfig {

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
            .group("public")
            .pathsToMatch("/api/v1/**")
            .build();
    }

    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
            .group("admin")
            .pathsToMatch("/api/admin/**")
            .build();
    }

    @Bean
    public GroupedOpenApi internalApi() {
        return GroupedOpenApi.builder()
            .group("internal")
            .pathsToMatch("/internal/**")
            .build();
    }
}

Swagger UI có dropdown "Select Group" — chọn public / admin / internal. Mỗi group có spec riêng tại /v3/api-docs/\{group\}.

Pattern thực tế: public API doc public, admin/internal API restrict access.

7. Hide internal endpoint

Mặc định mọi endpoint expose trong spec. Hide:

// Method-level
@Hidden
@GetMapping("/internal/admin")
public ... internal() { ... }

// Class-level
@Hidden
@RestController
public class InternalController { ... }

Hoặc qua config:

springdoc:
  packages-to-exclude: com.olhub.internal
  paths-to-exclude: /internal/**

Hide actuator endpoints (default Boot 3+ already hidden):

springdoc:
  show-actuator: false

8. Production deploy considerations

8.1 Disable trong production?

3 phương án:

Option 1 — Always enabled, public:

Pros: dev/QA test prod easily. Cons: expose API surface to attacker — discoverability.

Option 2 — Disable trong production:

springdoc:
  api-docs:
    enabled: false
  swagger-ui:
    enabled: false

Profile-specific:

# application-prod.yml
springdoc:
  api-docs:
    enabled: false
  swagger-ui:
    enabled: false

# application-dev.yml
springdoc:
  api-docs:
    enabled: true

Option 3 — Enabled but auth-protected:

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) {
        http.authorizeHttpRequests(auth -> auth
            .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
                .hasRole("ADMIN")
            .anyRequest().authenticated()
        );
        return http.build();
    }
}

Recommend Option 2 hoặc 3 cho production. Public API docs site (host trên CDN/separate site) cho external developer — không expose qua app server.

8.2 Versioning OpenAPI spec

Source of truth: OpenAPI spec generated từ controller — version theo Boot release. Lưu spec snapshot mỗi release:

# CI/CD pipeline
curl http://localhost:8080/v3/api-docs > openapi-v1.2.3.yaml
git add docs/api/openapi-v1.2.3.yaml
git commit -m "Snapshot API spec for v1.2.3"

Track diff giữa version → detect breaking change. Tool openapi-diff so 2 spec.

9. API-first vs code-first

2 workflow:

9.1 Code-first (springdoc default)

Developer code @RestController
    → springdoc auto-generate spec
    → Frontend đọc spec, implement

Pros:

  • Single source of truth (code).
  • Refactor code → spec auto update.
  • Quick start.

Cons:

  • Frontend đợi backend code xong mới có spec.
  • Spec design phụ thuộc code — không design contract trước.

Phù hợp: team nhỏ, app simple, backend lead workflow.

9.2 API-first

Designer write OpenAPI spec
    → Generator sinh interface Spring + DTO
    → Backend implement interface
    → Frontend đọc spec, implement song song

Tool: openapi-generator-maven-plugin:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>7.10.0</version>
    <executions>
        <execution>
            <goals><goal>generate</goal></goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/openapi.yaml</inputSpec>
                <generatorName>spring</generatorName>
                <apiPackage>com.olhub.api.generated</apiPackage>
                <modelPackage>com.olhub.api.generated.model</modelPackage>
                <configOptions>
                    <interfaceOnly>true</interfaceOnly>
                    <useSpringBoot3>true</useSpringBoot3>
                    <useJakartaEe>true</useJakartaEe>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

openapi.yaml define contract. Plugin sinh OrdersApi interface — controller implement:

@RestController
public class OrderController implements OrdersApi {
    @Override
    public ResponseEntity<OrderDto> getOrder(Long id) {
        return ResponseEntity.ok(orderService.findById(id));
    }
}

Pros:

  • Contract rõ ràng trước implement.
  • Frontend + backend song song.
  • Validate spec với linter (Spectral) — catch design issue.

Cons:

  • Setup complex.
  • Generator quirks (DTO không idiomatic Java).
  • Spec phải thiết kế kỹ — dễ overspec.

Phù hợp: team lớn, API public exposed, contract critical.

Khoá này (TaskFlow): code-first đơn giản. API-first introduced ở Module 11 (Microservices) khi cần.

10. Pitfall tổng hợp

Nhầm 1: Tin springdoc auto-detect mọi thứ. ✅ Auto đủ ~80%. Custom với @Operation, @Schema, @ApiResponses cho clarity.

Nhầm 2: Quên example trong @Schema. ✅ Add example — UI test "Try it out" tiện hơn 10x.

Nhầm 3: Expose Swagger UI production public. ✅ Disable hoặc auth-protect. Doc public trên CDN/separate site.

Nhầm 4: Spec version chỉ 1.0. ✅ Semver: v1.0.0, v1.1.0. Track breaking change qua minor/major bump.

Nhầm 5: Quên security scheme cho JWT API. ✅ Add @SecurityScheme — UI có "Authorize" button, dev test endpoint protected dễ.

Nhầm 6: Mix grouped API không filter. ✅ Group rõ ràng public/admin/internal — paths/packages explicit.

Nhầm 7: OpenAPI spec không lint. ✅ Run Spectral CI: catch undocumented response, missing operationId, naming inconsistent.

11. 📚 Deep Dive Spring Reference

📚 Tài liệu chính chủ

OpenAPI Specification:

springdoc-openapi:

Tooling:

Best practice:

Tool:

  • IntelliJ "Endpoints" tool window — visualize endpoints.
  • Bruno / Postman — import spec → auto-create collection.
  • redocly cli — render spec thành static HTML site.

12. Tóm tắt

  • OpenAPI Specification 3.1 — chuẩn JSON/YAML mô tả REST API. Fuel cho ecosystem tool: Swagger UI, OpenAPI Generator, Postman, Spectral.
  • springdoc-openapi 2.x — Boot 3 native, 1 dependency, auto-detect controller annotation → spec.
  • Default endpoint: /v3/api-docs (JSON), /v3/api-docs.yaml, /swagger-ui.html (UI).
  • Auto-detection đọc: controller annotation, parameter binding, validation constraint, return type, exception handler.
  • Customize annotation từ io.swagger.v3.oas.annotations:
    • @Operation — describe endpoint (summary, description, tags).
    • @Parameter — describe parameter (description, example).
    • @Schema — describe DTO field (description, example, constraint).
    • @ApiResponses — explicit response codes.
  • OpenAPI metadata global qua @Bean OpenAPI customOpenAPI() — info, server, contact, license.
  • Security scheme (@SecurityScheme) cho JWT/OAuth2 — UI có "Authorize" button.
  • Grouped API (GroupedOpenApi) — split public/admin/internal API thành group riêng.
  • Hide endpoint qua @Hidden hoặc springdoc.paths-to-exclude.
  • Production deploy: disable hoặc auth-protect Swagger UI. Public docs via CDN/separate site.
  • API-first vs code-first: code-first (springdoc default) cho team nhỏ. API-first (OpenAPI Generator) cho contract critical.

13. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao auto-generated OpenAPI spec từ springdoc thường đủ ~80% nhưng không 100%? Cho 3 ví dụ thông tin auto không capture.

Auto-detection lấy thông tin từ Java type system + Spring annotation:

  • URL path → từ @RequestMapping.
  • HTTP method → từ @GetMapping/@PostMapping/...
  • Parameter type, optional → từ @PathVariable/@RequestParam.
  • Schema field → từ DTO type (record component, class field).
  • Constraint → từ @NotNull/@Size/@Min/...
  • Response status → từ @ResponseStatus, ResponseEntity.

3 ví dụ thông tin auto KHÔNG capture (cần manual annotation):

  1. Description / summary của endpoint: auto chỉ có method name. Ngữ nghĩa "Get order by ID — returns 404 if not found, includes customer details" → cần @Operation(summary, description).
  2. Example value cho parameter/field: Java type không có "example". Frontend dev đọc spec không biết format đúng.
    @Schema(example = "ABC-1234") String sku;
    @Schema(example = "2026-04-15") LocalDate deliveryDate;
    @Parameter(example = "ACTIVE") OrderStatus status;
  3. Business meaning của response code khác 200: auto detect @ResponseStatus(NOT_FOUND) → 404. Nhưng "404 = order ID không tồn tại" vs "404 = endpoint không exist" cùng status code — khác semantic.
    @ApiResponses({
      @ApiResponse(responseCode = "200", description = "Order found"),
      @ApiResponse(responseCode = "404", description = "Order ID does not exist"),
      @ApiResponse(responseCode = "403", description = "Not authorized to view this order")
    })

Other thông tin manual:

  • Tag grouping (organize endpoints).
  • Security requirements per endpoint.
  • Deprecation note (@Deprecated annotation work but description thiếu).
  • External documentation link.
  • Custom schema constraint (regex pattern explanation, format like "ISO 8601 date").

Quy tắc: auto generate baseline. Bổ sung @Operation, @Schema(example), @ApiResponses cho 5-10 endpoint critical đầu tiên. Onboard dev mới đọc doc → chỉ ra chỗ nào unclear → bổ sung.

Q2
Production app có Swagger UI exposed public — có nên không? Tradeoff?

Default: NO. Disable hoặc auth-protect production.

Lý do disable:

  • API surface discoverability: attacker mở /swagger-ui.html → thấy mọi endpoint, parameter, schema. Tăng attack surface.
  • Validation rule leak: spec expose constraint (vd password "min 8 char + 1 uppercase") → dictionary attack design.
  • Internal endpoint expose: dễ quên hide endpoint admin/debug → public discoverable.
  • Performance: spec generation tốn CPU. Cron poll spec endpoint = wasted CPU.
  • Trust: "Try it out" feature có thể attacker dùng probe production directly.

Lý do enable:

  • Public API doc cho external developer (Stripe, GitHub style — họ host doc trên separate site, không expose qua app server).
  • Internal team test prod nhanh.
  • Auto-generated client SDK theo prod spec.

3 strategy:

  1. Disable hoàn toàn production:
    # application-prod.yml
    springdoc:
    api-docs:
      enabled: false
    swagger-ui:
      enabled: false
    Doc generated từ staging snapshot, host trên CDN (vd docs.olhub.org) — separate domain, separate auth.
  2. Auth-protect:
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) {
      http.authorizeHttpRequests(auth -> auth
          .requestMatchers("/swagger-ui/**", "/v3/api-docs/**")
              .hasRole("DEVELOPER")
          .anyRequest().permitAll()
      );
      return http.build();
    }
    Internal team có credential access.
  3. Path obscurity (weakest):
    springdoc:
    swagger-ui:
      path: /admin/internal-docs-x7k9q  # khó đoán URL
    api-docs:
      path: /admin/internal-spec-x7k9q
    Security through obscurity — không recommended primary defense.

Recommend cho TaskFlow capstone:

  • Dev/staging: Swagger UI enabled cho dev test.
  • Production: disable. CI/CD snapshot spec từ staging → host trên docs.olhub.org (Redocly hoặc Stoplight Elements).

Pattern này industry standard cho public API enterprise.

Q3
Team backend + frontend cần work song song. Backend chưa code xong API. So sánh code-first (springdoc) vs API-first (OpenAPI Generator) cho scenario này.

Scenario: parallel development. API-first thắng rõ.

AspectCode-first (springdoc)API-first (OpenAPI Generator)
Khi nào có specSau khi code Spring controllerTrước khi code (design phase)
Frontend đợi✅ phải đợi backend❌ song song với backend
Mock serverKhó — không có spec → không có mockEasy — Prism/Mockoon đọc spec, sinh mock
Contract changeBackend đổi code → frontend phát hiện sauSpec change → cả 2 team review trước
Contract driftCode thay đổi spec auto followGenerator regenerate interface → backend phải implement
Setup overhead1 dependencyOpenAPI Generator + define spec.yaml
Spec qualityAuto, có thể không idiomaticManual write — high quality, designed
Refactor costRefactor code, spec followRefactor spec, regenerate interface, fix backend

Workflow API-first cho parallel team:

  1. Day 1: Tech lead viết openapi.yaml (1 file, all endpoints + schema). Review với 2 team.
  2. Day 2: Sign off spec.
    • Backend: openapi-generator sinh OrdersApi interface + DTO. Backend implement interface.
    • Frontend: chạy Prism mock prism mock openapi.yaml --port 4010. Frontend dev với mock server.
  3. Day 3-5: Backend + frontend song song.
  4. Day 6: Frontend chuyển từ mock sang real backend. Đa phần work — vì cùng spec.
  5. Spec change mid-sprint: update openapi.yaml, regenerate cả 2 side. Diff explicit.

Workflow code-first cho parallel team (workaround):

  1. Backend code controller skeleton trả mock data (return hardcoded DTO).
  2. springdoc generate spec từ skeleton.
  3. Frontend đọc spec, dev.
  4. Backend implement real logic — interface không đổi.

Workaround code-first work nhưng yếu hơn — backend phải prioritize code skeleton trước, contract không document explicit.

Recommend cho parallel team: API-first nếu team lớn (5+ dev), API exposed external. Code-first nếu nhỏ (1-3 dev), iteration nhanh.

Khoá này (TaskFlow): code-first đơn giản. Module 11 (Microservices) introduce API-first cho service contract giữa team.

Q4
Implement security scheme cho app với JWT authentication. UI cần "Authorize" button.

Code đầy đủ:

@Configuration
@SecurityScheme(
  name = "bearerAuth",
  type = SecuritySchemeType.HTTP,
  scheme = "bearer",
  bearerFormat = "JWT",
  in = SecuritySchemeIn.HEADER,
  description = "JWT token from /auth/login endpoint"
)
public class OpenApiConfig {

  @Bean
  public OpenAPI customOpenAPI() {
      return new OpenAPI()
          .info(new Info()
              .title("TaskFlow API")
              .version("v1.0"))
          .addSecurityItem(new SecurityRequirement().addList("bearerAuth"));    // global apply
  }
}

Apply security per controller (override global):

// Public endpoint - no auth required
@RestController
@RequestMapping("/api/auth")
public class AuthController {

  @SecurityRequirements    // empty — override global, no auth
  @PostMapping("/login")
  public TokenDto login(@Valid @RequestBody LoginRequest req) { ... }
}

// Protected endpoint - bearerAuth required (inherit global, can be explicit)
@SecurityRequirement(name = "bearerAuth")
@RestController
@RequestMapping("/api/orders")
public class OrderController { ... }

UI experience:

  1. Mở /swagger-ui.html.
  2. Top right: button "Authorize" (lock icon).
  3. Click → modal popup "Bearer authentication: enter JWT token".
  4. User paste token (vd eyJhbGc...).
  5. "Authorize" button.
  6. Mọi "Try it out" sau đó tự include header Authorization: Bearer eyJhbGc....

Workflow dev:

  1. Login: POST /api/auth/login với credential. Response trả JWT.
  2. Copy JWT từ response.
  3. Click "Authorize" UI → paste JWT.
  4. Test mọi protected endpoint với "Try it out".

Tiện hơn 10x so với manual set header trong Postman.

OAuth2 với authorization code flow:

@SecurityScheme(
  name = "oauth2",
  type = SecuritySchemeType.OAUTH2,
  flows = @OAuthFlows(
      authorizationCode = @OAuthFlow(
          authorizationUrl = "https://auth.olhub.org/oauth2/authorize",
          tokenUrl = "https://auth.olhub.org/oauth2/token",
          scopes = {
              @OAuthScope(name = "read", description = "Read access"),
              @OAuthScope(name = "write", description = "Write access")
          }
      )
  )
)

UI redirect user đến auth server, callback nhận code, exchange token. OAuth2 client ID/secret config trong UI.

Q5
Big app có public API + admin API + internal API. Setup spec group ra sao? Lợi ích cho doc và security?

Setup grouped OpenAPI:

@Configuration
public class OpenApiConfig {

  @Bean
  public GroupedOpenApi publicApi() {
      return GroupedOpenApi.builder()
          .group("public")
          .displayName("Public API (v1)")
          .pathsToMatch("/api/v1/**")
          .packagesToScan("com.olhub.api.public")
          .build();
  }

  @Bean
  public GroupedOpenApi adminApi() {
      return GroupedOpenApi.builder()
          .group("admin")
          .displayName("Admin API")
          .pathsToMatch("/api/admin/**")
          .packagesToScan("com.olhub.api.admin")
          .addOpenApiCustomizer(openApi -> openApi.info(
              new Info().title("Admin API — INTERNAL").version("v1")
          ))
          .build();
  }

  @Bean
  public GroupedOpenApi internalApi() {
      return GroupedOpenApi.builder()
          .group("internal")
          .displayName("Service-to-Service Internal")
          .pathsToMatch("/internal/**")
          .build();
  }
}

Swagger UI: dropdown top-right "Select Definition":

  • Public API (v1) → endpoint /api/v1/**.
  • Admin API → endpoint /api/admin/**.
  • Service-to-Service Internal → endpoint /internal/**.

Spec endpoints separate:

  • /v3/api-docs/public — public spec.
  • /v3/api-docs/admin — admin spec.
  • /v3/api-docs/internal — internal spec.

Lợi ích cho doc:

  • Cleanly separated: public dev không thấy admin endpoint trong UI.
  • Different titling/branding: public title "OLHub TaskFlow API", admin title "Admin Console (Internal Use)".
  • Different versioning: public v1.0 stable contract, admin v1.5 evolve fast.
  • Generate separate client SDK: public SDK ship cho external, admin SDK chỉ internal.

Lợi ích cho security:

  • Selective exposure:
    # application-prod.yml
    springdoc:
    show-actuator: false
    swagger-ui:
      disable-swagger-default-url: true
      urls:
        - name: Public API
          url: /v3/api-docs/public
        # Admin + internal NOT exposed in production UI
    UI production chỉ show public group. Admin + internal spec endpoint disabled hoặc auth-protected.
  • Auth-protect specific group:
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) {
      http.authorizeHttpRequests(auth -> auth
          .requestMatchers("/v3/api-docs/admin", "/v3/api-docs/internal")
              .hasRole("ADMIN")
          .requestMatchers("/v3/api-docs/public").permitAll()
          ...
      );
    }
  • CI/CD snapshot: snapshot riêng spec mỗi group → public spec ship cho external doc site, admin spec ship cho internal wiki.

Pattern enterprise tương tự: Stripe có 2 spec — public API + Connect API. Group cho phép modular API surface.

Bài tiếp theo: Mini-challenge — TaskFlow REST API v1 capstone

Bài này có giúp bạn hiểu bản chất không?

Bình luận (0)

Đang tải...