Building REST APIs

Documenting APIs with OpenAPI

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

আমরা এখন পর্যন্ত আমাদের HTTP API-এর প্রায় পুরো public contract design করেছি।

আমাদের endpoints:

/api/v1/products

/api/v1/inventory

/api/v1/orders

আমরা define করেছি:

HTTP methods

request DTOs

response DTOs

status codes

validation

pagination

error format

API versioning

এখন প্রশ্ন হলো:

অন্য একজন engineer কীভাবে এই contract বুঝবে?

শুধু Controller code পড়ে?

Confluence page পড়ে?

Slack message খুঁজে?

Frontend source code inspect করে?

Production-quality API-এর জন্য contract এমন format-এ describe করা দরকার যা:

মানুষ পড়তে পারে

tooling বুঝতে পারে

documentation generate করা যায়

client generation করা যায়

contract review করা যায়

OpenAPI এই problem solve করার জন্য একটি standard API description format। OpenAPI Specification HTTP API-কে language-independent machine-readable contract হিসেবে describe করে; current published specification হলো OpenAPI 3.2.0।

এই lesson-এর goal:

আমাদের /api/v1 HTTP contract-কে OpenAPI দিয়ে document করা—without letting documentation annotations become the source of business behaviour.


OpenAPI Is a Contract Description

Suppose we have:

POST /api/v1/orders

A developer needs to know:

What request body is accepted?

Which fields are required?

What does success return?

What does 400 mean?

What does 404 mean?

What does 409 mean?

Is authentication required?

What error shape is returned?

OpenAPI lets us describe all of that in one structured specification।

Conceptually:

paths:
  /api/v1/orders:
    post:
      ...

OpenAPI Is Not Swagger UI

These terms are often mixed together।

They are different concepts।

OpenAPI
→ API description specification

A UI such as Swagger UI or Scalar can render an OpenAPI document into interactive documentation।

Spring tooling such as springdoc-openapi can inspect Spring Boot MVC configuration and annotations to generate an OpenAPI document; its official documentation describes generated JSON API descriptions and interactive UI integrations.

Think:

Spring Controllers + DTOs + metadata
             ↓
        OpenAPI document
             ↓
     documentation UI

The OpenAPI document is the important contract artifact।

The UI is one way to view it।


What OpenAPI Can Describe

Our specification can describe:

API information

paths

HTTP methods

path parameters

query parameters

request bodies

response bodies

schemas

HTTP status codes

authentication requirements

error responses

These concepts are defined directly by the OpenAPI Specification.


Start With API Information

An OpenAPI document begins with basic metadata।

Conceptually:

openapi: 3.2.0

info:
  title: Order Management API
  version: 1.0.0
  description: >
    HTTP API for product browsing,
    inventory management,
    order workflows,
    and payment operations.

Notice:

info.version

is document/API metadata।

It should not be confused with:

/api/v1

route version semantics।


Avoid Confusing Three Versions

We may have:

OpenAPI specification version
→ 3.x
our public API major version
→ v1
application release version
→ whatever build is deployed

These are separate concerns।


Paths

Our current public API direction can be represented as:

paths:

  /api/v1/products:
    get:
      ...
    post:
      ...

  /api/v1/products/{productId}:
    get:
      ...
    patch:
      ...

  /api/v1/products/{productId}/deactivate:
    post:
      ...

  /api/v1/inventory:
    get:
      ...

  /api/v1/inventory/{productId}:
    get:
      ...
    put:
      ...

  /api/v1/orders:
    get:
      ...
    post:
      ...

  /api/v1/orders/{orderId}:
    get:
      ...

  /api/v1/orders/{orderId}/cancel:
    post:
      ...

  /api/v1/orders/{orderId}/pay:
    post:
      ...

This is valuable because the complete HTTP surface becomes visible without reading Java implementation।


Path Parameters

For:

GET /api/v1/orders/{orderId}

OpenAPI should describe:

parameters:
  - name: orderId
    in: path
    required: true
    schema:
      type: string

We currently know:

OrderId is represented as a string at the HTTP boundary

but we have not finalized a UUID/prefix/numeric format।

Therefore documentation should not invent:

format: uuid

unless that decision is actually made।


Documentation Must Follow the Contract

This principle matters:

OpenAPI describes decisions. It should not silently make decisions for us.

If ProductId format is not decided:

type: string

is enough।

Do not add a regex simply because OpenAPI supports patterns।


Request Body

Our Create Product request:

{
  "name": "Mechanical Keyboard",
  "price": 100.00
}

can be described conceptually as:

requestBody:
  required: true
  content:
    application/json:
      schema:
        $ref: "#/components/schemas/CreateProductRequest"

Then:

components:
  schemas:

    CreateProductRequest:
      type: object
      required:
        - name
        - price
      properties:
        name:
          type: string
        price:
          type: number

The schema describes the public request contract।


Request Schema Should Match Authority

Our Order creation schema should not contain:

customerId

status

unitPrice

total

because the client does not control them।

Correct conceptual schema:

CreateOrderRequest:
  type: object
  required:
    - items
  properties:
    items:
      type: array
      minItems: 1
      items:
        $ref: "#/components/schemas/CreateOrderItemRequest"

and:

CreateOrderItemRequest:
  type: object
  required:
    - productId
    - quantity
  properties:
    productId:
      type: string
    quantity:
      type: integer
      minimum: 1

OpenAPI documentation should reinforce the same server-authority boundary as our DTO design।


Do Not Document Fields the Client Cannot Send

Bad OpenAPI:

CreateOrderRequest:
  properties:
    customerId:
      type: string
    status:
      type: string
    total:
      type: number

even if implementation ignores them।

Documentation would tell clients:

These are valid request fields.

That creates a misleading contract।

Documentation and implementation must agree।


Response Schemas

Our Order response conceptually contains:

{
  "id": "O-1001",
  "status": "UNPAID",
  "items": [
    {
      "productId": "P-100",
      "quantity": 2,
      "unitPrice": 20.00,
      "total": 40.00
    }
  ],
  "total": 40.00
}

We can describe:

OrderResponse:
  type: object
  required:
    - id
    - status
    - items
    - total
  properties:

    id:
      type: string

    status:
      type: string
      enum:
        - UNPAID
        - PAID
        - CANCELLED

    items:
      type: array
      items:
        $ref: "#/components/schemas/OrderItemResponse"

    total:
      type: number

Now API consumers know exactly which lifecycle values currently exist।


Public Enums Deserve Care

Once OpenAPI says:

enum:
  - UNPAID
  - PAID
  - CANCELLED

those values are visibly part of the contract।

That reinforces the versioning lesson:

Adding or changing enum semantics deserves compatibility review।

Documentation makes implicit coupling visible।


Reusable Schemas

Instead of redefining:

OrderResponse

inside every endpoint, OpenAPI allows reusable components।

Conceptually:

components:
  schemas:
    OrderResponse:
      ...

Then:

$ref: "#/components/schemas/OrderResponse"

can be reused across:

create Order

get Order

cancel Order

pay Order

if those operations intentionally return the same representation।


Reuse Only When Contracts Are Actually the Same

Do not reuse one giant:

OrderDto

simply to reduce OpenAPI YAML।

If:

GET /orders

eventually returns OrderSummaryResponse

while:

GET /orders/{id}

returns detailed OrderResponse,

document them separately।

OpenAPI reuse should reflect semantic reuse, not force it।


Success Responses

Create Order:

responses:

  "201":
    description: Order created
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/OrderResponse"

We can also document:

Location

response header because successful creation may identify:

/api/v1/orders/{orderId}

GET Response

For:

GET /api/v1/orders/{orderId}

documentation can say:

"200":
  description: Order returned successfully
  content:
    application/json:
      schema:
        $ref: "#/components/schemas/OrderResponse"

This defines observable behaviour, not implementation details।


Document Expected Errors

A professional specification should not document only:

200
201

Expected failures are equally important।

For Order creation we currently have meaningful cases such as:

400 → invalid request

401 → authentication required

404 → Product missing

409 → Product not orderable / insufficient Inventory

500 → unexpected server failure

These belong in API documentation।


Our Canonical Problem Schema

Our LiveKlass problem response has:

type
title
status
detail
instance
code
errors?

Conceptually:

ApiProblem:
  type: object
  required:
    - type
    - title
    - status
    - detail
    - instance
    - code

  properties:

    type:
      type: string
      format: uri

    title:
      type: string

    status:
      type: integer

    detail:
      type: string

    instance:
      type: string

    code:
      type: string

    errors:
      type: array
      items:
        $ref: "#/components/schemas/FieldProblem"

FieldProblem

FieldProblem:
  type: object
  required:
    - field
    - code
    - message

  properties:

    field:
      type: string

    code:
      type: string

    message:
      type: string

This matches our canonical API contract।


Validation Problem Example

OpenAPI can include an example:

example:
  type: https://api.liveklass.io/problems/validation-error
  title: Validation failed
  status: 400
  detail: One or more fields are invalid.
  instance: /api/v1/orders
  code: VALIDATION_ERROR
  errors:
    - field: items[0].quantity
      code: INVALID_VALUE
      message: Quantity must be greater than 0.

Examples are valuable because a schema tells clients:

what can exist

while an example shows:

what a real response looks like

Reusable Error Responses

Because many endpoints return the same broad problem schema, we can reuse response components।

Conceptually:

components:

  responses:

    ValidationError:
      description: Request validation failed
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ApiProblem"

Likewise:

Unauthorized

Forbidden

InternalError

can potentially share reusable definitions।


Don't Over-Generalize Business Errors

We could create one generic:

ConflictError

for every 409।

But clients still need to understand:

INSUFFICIENT_INVENTORY

versus:

ORDER_NOT_CANCELLABLE

The body schema may be shared, while operation descriptions/examples document the possible application code values।


Document Error Codes

For:

POST /api/v1/orders/{orderId}/cancel

a 409 response should document possible:

ORDER_NOT_CANCELLABLE

rather than merely:

409 Conflict

This makes our machine-readable error vocabulary discoverable।


Pagination Parameters

Our current pagination contract:

page
→ default 0
→ minimum 0

size
→ default 20
→ minimum 1
→ maximum 100

OpenAPI should describe exactly that।

Conceptually:

parameters:

  - name: page
    in: query
    required: false
    schema:
      type: integer
      minimum: 0
      default: 0

  - name: size
    in: query
    required: false
    schema:
      type: integer
      minimum: 1
      maximum: 100
      default: 20

Now clients don't need to guess pagination rules।


Paginated Response

Our page envelope:

{
  "items": [],
  "page": 0,
  "size": 20,
  "totalItems": 0,
  "totalPages": 0
}

should also have an explicit schema।

For Product:

ProductPageResponse:
  type: object
  required:
    - items
    - page
    - size
    - totalItems
    - totalPages

  properties:

    items:
      type: array
      items:
        $ref: "#/components/schemas/ProductResponse"

    page:
      type: integer

    size:
      type: integer

    totalItems:
      type: integer
      format: int64

    totalPages:
      type: integer

Generic Concepts vs Concrete OpenAPI Schemas

In Java we might eventually use:

PageResponse<T>

But generated OpenAPI needs concrete schemas consumers can understand, such as:

ProductPageResponse

OrderPageResponse

Tooling may generate these from generic Java models, but we should review the resulting contract rather than assume generic inference is perfect।


Authentication Documentation

Our protected endpoints eventually require authenticated identity।

OpenAPI supports reusable security schemes for documenting how operations are authenticated.

Conceptually, if the application uses Bearer token authentication:

components:

  securitySchemes:

    bearerAuth:
      type: http
      scheme: bearer

Then an operation can declare:

security:
  - bearerAuth: []

Do Not Invent Authentication Details

We know our application uses an existing identity mechanism and eventually Spring Security।

But exact token details should reflect the real authentication contract।

Do not document:

JWT claims

issuer

OAuth flow

token URL

unless those are actual decisions।

OpenAPI documentation must describe reality।


Authentication vs Authorization Documentation

Security scheme can communicate:

authentication required

But role requirements also matter։

For example:

POST /products
→ ADMIN only

Operation description should make that clear।

Likewise:

POST /orders
→ authenticated CUSTOMER

and:

GET own Order
→ ownership enforced

OpenAPI cannot replace our authorization implementation, but it should communicate the intended contract।


Don't Put customerId Back Into the API Documentation

Because authentication owns customer identity, this would be wrong:

parameters:
  - name: customerId
    in: query

for customer Order history।

Correct documentation:

GET /api/v1/orders

returns Orders scoped to the authenticated customer।

That security rule is part of endpoint description, not a caller-controlled filter।


Operation IDs

OpenAPI operations can have stable identifiers such as:

operationId: createOrder

or:

operationId: cancelOrder

These can be useful for:

documentation

client generation

tooling

Use meaningful names that represent application operations।

Avoid auto-generated names such as:

postUsingPOST_3

if we have control over them।


Operation Summary and Description

Good documentation:

summary: Create an order

description: >
  Creates an unpaid order for the authenticated customer
  using current server-side product prices.
  Inventory is consumed as part of successful creation.

This communicates meaningful behaviour।


Documentation Should Explain Important Semantics

For Order creation, useful documentation includes:

authenticated Customer owns the Order

client price is ignored/not accepted

server uses current Product price

duplicate Product IDs are invalid

successful creation consumes Inventory

operation is all-or-nothing

These behaviours are far more valuable than repeating:

POST means POST.

Don't Turn OpenAPI Into an RFC

However, OpenAPI is not the place for the entire technical design।

It should not contain long explanations about:

why we chose modular monolith

why Inventory is separate from Product

transaction implementation details

JPA mapping strategy

database indexes

Those belong in:

RFCs

ADRs

technical documentation

OpenAPI documents the client-facing contract।


OpenAPI vs RFC

Think:

RFC
→ why/how we designed the system
OpenAPI
→ what HTTP clients can rely on

Both matter।

They solve different documentation problems।


Code-First Documentation

With Spring Boot, one approach is:

Controller + DTOs + annotations
        ↓
OpenAPI generation

springdoc-openapi is designed to generate OpenAPI descriptions from Spring Boot applications by examining Spring configuration and annotations.

This is often called a code-first approach।


Contract-First Documentation

Another approach:

OpenAPI YAML
     ↓
implementation

where specification is designed first and code follows it।

This can be valuable when:

multiple teams integrate independently

clients are generated before implementation

API governance is strict

Which Approach Are We Using?

Our course workflow already does:

Requirement
    ↓
RFC / API design
    ↓
engineering tickets
    ↓
implementation

So conceptually we are contract-driven

But in the Spring project, we can use springdoc-openapi to generate the machine-readable specification from our actual Controllers/DTOs plus explicit documentation metadata।

The important part is not whether the YAML or Java file was typed first।

The important part is:

The reviewed API design and generated documentation must agree.


Generated Does Not Mean Correct

Suppose tooling sees:

public OrderResponse create(...) {
}

It may infer a response schema।

But tooling cannot automatically know all business semantics such as:

409 means INSUFFICIENT_INVENTORY

Customer can only read own Orders

Product deactivation preserves historical Orders

We still need intentional documentation।


Annotation Explosion

A Controller can become difficult to read if every method contains dozens of documentation annotations।

For example conceptually:

@Operation(...)
@ApiResponses(...)
@RequestBody(...)
@Parameter(...)
@Schema(...)
...

Documentation metadata has value, but Controller readability also matters।

Use annotations where they provide real contract information, and place reusable schema information on DTOs/configuration where appropriate।

Do not turn Handler logic into an OpenAPI wall।


Springdoc Metadata

Springdoc supports supplementing generated descriptions with OpenAPI/Swagger annotations, and can derive information from Spring application structure.

Conceptually:

@Operation(
        summary = "Create an order"
)
@PostMapping
public ...

can improve generated operation documentation।


DTO Schema Metadata

If a field meaning isn't obvious:

@Schema(
        description =
                "Quantity of the product to order"
)
Integer quantity

may help।

But avoid redundant annotations like:

description = "The quantity field"

Good documentation explains semantics, not syntax already visible from the schema।


Examples Can Be More Valuable Than Descriptions

For ProductId:

example = P-100

can make docs easier to understand।

For validation problem:

VALIDATION_ERROR

with nested field errors is especially useful as a complete example।

Examples should be realistic but not mistaken for fixed business values।


Avoid Fake Requirements in Examples

If we show:

maximumQuantity = 10

in a schema example, students/consumers may infer it is an actual rule।

Our current requirements do not define a maximum order quantity।

Examples should not accidentally become shadow requirements।


Document Content Types

Successful JSON API responses use:

application/json

Our canonical error documents use:

application/problem+json

Documenting both makes the contract more precise।


Complete Conceptual Order Endpoint

A simplified OpenAPI fragment might look like:

paths:

  /api/v1/orders:

    post:

      operationId: createOrder

      summary: Create an order

      security:
        - bearerAuth: []

      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderRequest"

      responses:

        "201":
          description: Order created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderResponse"

        "400":
          description: Request validation failed
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiProblem"

        "401":
          description: Authentication required
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiProblem"

        "404":
          description: A referenced product could not be found
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiProblem"

        "409":
          description: >
            Order creation conflicts with current
            product or inventory state
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiProblem"

        "500":
          description: Unexpected server failure
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ApiProblem"

This already tells another engineer a lot about the operation।


But 409 Still Needs Application Meaning

The specification should also explain possible code values such as:

PRODUCT_NOT_ORDERABLE

INSUFFICIENT_INVENTORY

Otherwise a client only knows:

some conflict happened

Our custom code field is part of the public contract and should be documented intentionally।


Product Endpoint Example

POST /api/v1/products

should document:

201 PRODUCT CREATED

400 VALIDATION_ERROR / INVALID_REQUEST

401 AUTHENTICATION_REQUIRED

403 ACCESS_DENIED

500 INTERNAL_ERROR

No:

customerId

No invented Inventory initialization।

OpenAPI should reflect our accepted business contract exactly।


Inventory Endpoint Example

For:

PUT /api/v1/inventory/{productId}

document:

availableQuantity
→ required
→ integer
→ minimum 0

And expected responses:

200

400 VALIDATION_ERROR

401

403

404

500

Again, no speculative:

inventory movement history

warehouse ID

adjustment reason

because those features don't exist।


Cancellation Endpoint Example

For:

POST /api/v1/orders/{orderId}/cancel

the most valuable documentation is not the empty request body।

It is the behaviour:

only authenticated owner may cancel

only UNPAID Order may cancel

successful cancellation restores Inventory

PAID/CANCELLED conflict returns
ORDER_NOT_CANCELLABLE

This is what consumers need to integrate correctly।


Payment Documentation Is Intentionally Incomplete

We already know:

POST /api/v1/orders/{orderId}/pay

exists conceptually।

But the exact Payment Provider contract is deferred।

Therefore don't invent OpenAPI request fields such as:

cardNumber

paymentMethodToken

providerSessionId

until integration requirements are known।

OpenAPI documentation should remain honest about what is actually designed।


OpenAPI Is Executable Documentation, Not Truth by Magic

A generated document can still be wrong।

Examples:

Controller accepts field but docs hide it

docs say 404 but runtime returns 500

docs say max size 100 but implementation allows 1000

error schema says application/json
but runtime returns something else

The specification becomes useful only if we keep it aligned with implementation।


Contract Tests Help

Important API behaviour can be verified through tests:

route exists

request validation matches docs

response shape matches DTO

status codes match contract

error structure matches ApiProblem

We don't need to test every generated OpenAPI byte।

But contract-critical behaviour should be protected।


OpenAPI Validation in CI

As the project matures, CI can:

generate OpenAPI

validate specification

compare important contract changes

Potential breaking changes can then be reviewed deliberately।

We do not need to build sophisticated API governance infrastructure in the first ticket।

But OpenAPI gives us the artifact needed for that later։


Generated Client Code

A machine-readable OpenAPI contract can also be used by tooling to generate client code because the specification exposes paths and schemas in a language-independent format.

This can reduce duplicated handwritten client models।

But generated clients are only as good as the specification।

Poor schema:

object

everywhere produces poor clients।

Strong explicit DTOs produce stronger generated contracts।


OpenAPI Should Not Dictate Domain Design

Avoid reasoning:

OpenAPI generated this schema, so our Order domain must match it.

Direction remains:

Requirements
    ↓
Domain / API design
    ↓
Implementation
    ↓
OpenAPI representation

Not:

Documentation generator
    ↓
Domain architecture

Exposing OpenAPI in Production

Whether interactive documentation endpoints are publicly accessible in production is an operational/security decision।

The machine-readable API description may be useful internally, publicly, or behind access controls depending on product needs।

Do not assume:

Swagger/Scalar UI must always be public in production

because a dependency makes it available locally।

Environment exposure should be deliberate।


Local Development Experience

For developers, interactive documentation can be extremely useful:

inspect endpoints

inspect schemas

see examples

try requests

Springdoc currently provides generated OpenAPI endpoints and supports interactive documentation integrations for Spring Boot applications.

This can make local development and frontend integration easier।


Don't Replace Automated Tests With "Try It"

Interactive API documentation is useful for exploration।

It does not replace:

unit tests

integration tests

contract tests

Clicking an endpoint successfully once does not prove:

validation

authorization

concurrency

error cases

work correctly।


Don't Replace Requirements With OpenAPI Either

OpenAPI can say:

quantity minimum = 1

But the reason this rule exists comes from requirements/domain design।

The specification captures the contract after the decision।

It doesn't replace the engineering reasoning behind it।


Documentation Quality Checklist

For each operation, ask:

Is the HTTP method and path documented?

Are all path/query parameters documented?

Is the request body schema accurate?

Are required fields correct?

Are server-controlled fields absent from request schemas?

Is the success status documented?

Is the success response schema documented?

Are expected 4xx outcomes documented?

Is the canonical ApiProblem schema used?

Are meaningful application error codes documented?

Is authentication requirement visible?

Are authorization/ownership semantics explained?

Are pagination defaults and limits documented?

Have we avoided documenting features
that do not actually exist?

Common Mistake 1 — Swagger UI Is Treated as OpenAPI

The specification is the contract; interactive UI is a renderer/consumer of that contract।


Common Mistake 2 — Only Success Responses Are Documented

Expected client and business failures are part of the API too।


Common Mistake 3 — Generic object Schemas

Clients cannot understand or generate useful types from vague contracts।


Common Mistake 4 — Request Schema Mirrors Domain Entity

Server-controlled fields accidentally become documented client input।


Common Mistake 5 — JPA Entity Generates Public Schema

Persistence implementation leaks into API documentation।


Common Mistake 6 — Documentation Invents Rules

Don't add arbitrary ID patterns, quantity limits, search filters, or Payment fields just to make the schema look complete।


Common Mistake 7 — Pagination Is Undocumented

Clients then guess default size, maximum size, and page numbering।


Common Mistake 8 — Error Body Is Documented as "string"

Our canonical Problem Details contract deserves a real schema।


Common Mistake 9 — Authentication Requirement Is Missing

Clients should know which operations require identity and permissions।


Common Mistake 10 — Generated Documentation Is Never Reviewed

Automation reduces manual work; it does not guarantee semantic correctness।


Our OpenAPI Documentation Direction

For the v1 API we will document:

/api/v1/products

/api/v1/inventory

/api/v1/orders

including:

request schemas

response schemas

page/size pagination

authentication requirements

authorization expectations

HTTP statuses

canonical application/problem+json errors

stable application error codes

We will not document speculative:

Customer CRUD

OrderItem CRUD

Product delete

Product reactivation

warehouse management

Inventory reservations

refunds

Payment Provider request fields

search/filter combinations not yet required

Responsibility Map

API Design

Decides:

what the client contract should be

Handler and DTOs

Implement:

that transport contract

OpenAPI

Describes:

that contract in machine-readable form

Tests

Verify:

runtime behaviour still follows the contract

RFCs and ADRs

Explain:

why important design choices were made

These artifacts complement each other।

They are not replacements for each other।


Engineering Principle

The core principle:

OpenAPI documents the contract consumers can rely on; it should not expose or dictate the implementation behind that contract.

Another:

Generated documentation still requires engineering judgment—tools can discover structures, but they cannot decide our business semantics for us.

And:

An API is not fully documented if only successful requests are described; validation, authentication, business conflicts, pagination, and error codes are part of the contract too.


Summary

In this lesson, we learned that:

  • OpenAPI provides a language-independent machine-readable description of an HTTP API.
  • OpenAPI and interactive documentation tools such as Swagger UI or Scalar are different concepts.
  • Our public API is versioned under /api/v1.
  • OpenAPI should document paths, methods, parameters, bodies, responses, schemas, and security requirements.
  • Request schemas must contain only fields callers are legitimately allowed to provide.
  • CreateOrderRequest must not expose customerId, price, total, or status.
  • Response schemas can describe server-controlled state such as Order status and purchase-time prices.
  • Reusable schemas belong under OpenAPI components when contracts genuinely match.
  • Public enum values should be documented intentionally because consumers may depend on them.
  • Successful creation should document 201, the response model, and where appropriate the Location header.
  • Expected 400, 401, 403, 404, 409, and 500 responses should also be documented.
  • Our canonical ApiProblem contains type, title, status, detail, instance, code, and optional errors.
  • Validation field errors contain field, code, and message.
  • Error responses use application/problem+json.
  • Application error codes such as INSUFFICIENT_INVENTORY and ORDER_NOT_CANCELLABLE should be visible in the API contract.
  • Pagination documentation must define page, size, defaults, bounds, and page response metadata.
  • Authentication can be represented through an OpenAPI security scheme while authorization semantics still need clear operation documentation.
  • Customer ownership must not become a caller-controlled customerId query parameter.
  • OpenAPI documentation is different from an RFC: OpenAPI describes what clients can rely on; RFCs explain engineering decisions.
  • springdoc-openapi can generate OpenAPI descriptions from Spring Boot applications and enrich them with metadata.
  • Generated documentation must still be reviewed because tooling cannot infer every business rule.
  • OpenAPI should not expose persistence entities or dictate domain design.
  • We should not document speculative functionality merely to make the API appear comprehensive.
  • Tests should protect the runtime contract represented by the documentation.

This completes:

Module 5 — Building REST APIs

We now have the HTTP foundation needed to move from API design into persistence.

Next:

Module 6 — Persistence with PostgreSQL

Lesson 1: Why Our Application Needs Persistence

There we will examine what state our Order Management Backend must preserve, why in-memory Java objects are insufficient, what PostgreSQL is responsible for, and—equally important—what responsibilities do not belong to the database.