Your API has many endpoints. Other developers need to know how to use them. They need to know the request format, response format, required headers, and possible error codes. Writing documentation by hand is tedious and quickly becomes outdated.
In this tutorial, you will add OpenAPI documentation and Swagger UI to your Ktor API. Developers can browse your API at /docs and try out endpoints directly from the browser.
What is OpenAPI?
OpenAPI (formerly Swagger) is a standard format for describing REST APIs. It is a YAML or JSON file that lists all endpoints, request bodies, response schemas, authentication, and more.
Swagger UI is a web interface that reads the OpenAPI file and displays an interactive API explorer.
Dependencies
dependencies {
implementation("io.ktor:ktor-server-openapi:$ktorVersion")
implementation("io.ktor:ktor-server-swagger:$ktorVersion")
}
Write the OpenAPI Spec
Create the file at src/main/resources/openapi/documentation.yaml:
openapi: 3.0.3
info:
title: Ktor Tutorial API
description: REST API built with Ktor
version: 1.0.0
servers:
- url: http://localhost:8080
description: Local development
paths:
/api/auth/register:
post:
tags: [Authentication]
summary: Register a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
responses:
'201':
description: User registered
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'400':
description: Validation error
'409':
description: Email already exists
/api/auth/login:
post:
tags: [Authentication]
summary: Login and get tokens
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Login successful
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
'401':
description: Invalid credentials
/api/notes:
get:
tags: [Notes]
summary: List notes with filtering
parameters:
- name: page
in: query
schema: { type: integer, default: 1 }
- name: size
in: query
schema: { type: integer, default: 10 }
- name: tag
in: query
schema: { type: string }
responses:
'200':
description: List of notes
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/NoteResponse'
post:
tags: [Notes]
summary: Create a note
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateNoteRequest'
responses:
'201':
description: Note created
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
RegisterRequest:
type: object
required: [name, email, password]
properties:
name: { type: string, example: Alex }
email: { type: string, format: email }
password: { type: string, minLength: 8 }
LoginRequest:
type: object
required: [email, password]
properties:
email: { type: string }
password: { type: string }
TokenResponse:
type: object
properties:
accessToken: { type: string }
refreshToken: { type: string }
expiresIn: { type: integer }
NoteResponse:
type: object
properties:
id: { type: integer }
title: { type: string }
content: { type: string }
tags:
type: array
items: { type: string }
CreateNoteRequest:
type: object
required: [title, content]
properties:
title: { type: string }
content: { type: string }
tags:
type: array
items: { type: string }
This is a simplified example. The full spec in the source code documents all endpoints.
Serve the Docs
Create a plugin to serve the OpenAPI spec and Swagger UI:
fun Application.configureOpenApi() {
routing {
// Serve OpenAPI spec at /openapi
openAPI(path = "openapi", swaggerFile = "openapi/documentation.yaml")
// Serve Swagger UI at /docs
swaggerUI(path = "docs", swaggerFile = "openapi/documentation.yaml") {
version = "5.17.14"
}
}
}
Install it in your application module:
fun Application.module() {
// ... other plugins
configureOpenApi()
configureRouting()
}
Now visit http://localhost:8080/docs to see the interactive API documentation.
Documenting Endpoints
Tags
Group endpoints with tags:
paths:
/api/auth/register:
post:
tags: [Authentication]
Security
Mark protected endpoints:
/api/auth/me:
get:
security:
- bearerAuth: []
Request/Response Examples
Add examples to make the docs more helpful:
RegisterRequest:
type: object
properties:
name:
type: string
example: Alex
email:
type: string
example: alex@example.com
password:
type: string
example: password123
Error Responses
Document error responses:
responses:
'400':
description: Validation error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: Unauthorized
'404':
description: Not found
Generating Client SDKs
The OpenAPI spec can generate client code. Use openapi-generator:
# Generate TypeScript client
npx @openapitools/openapi-generator-cli generate \
-i http://localhost:8080/openapi \
-g typescript-axios \
-o ./client
# Generate Kotlin client
npx @openapitools/openapi-generator-cli generate \
-i http://localhost:8080/openapi \
-g kotlin \
-o ./kotlin-client
This generates a type-safe client that matches your API exactly.
Keeping Docs in Sync
The biggest challenge with OpenAPI is keeping the spec file in sync with your code. Some strategies:
- Write the spec first (API-first design) — then implement the endpoints to match
- Update the spec when you change code — add it to your code review checklist
- Use tests to validate — test that endpoints match the documented schemas
Common Mistakes
- Not documenting error responses — Developers need to know what errors to handle.
- Missing response schemas — Without schemas, the Swagger UI cannot show response examples.
- Outdated documentation — If you change an endpoint and forget to update the spec, developers will get confused.
- Not using tags — Without tags, all endpoints appear in one long list.
Source Code
You can find the source code for this tutorial on GitHub:
github.com/kemalcodes/ktor-tutorial — Branch: tutorial-16-openapi
What’s Next?
You have API documentation at /docs. In the next tutorial, you will add HTMX with Ktor — building a server-rendered admin dashboard with dynamic updates.
Related Articles
- Ktor Tutorial #14: Security — CORS, rate limiting, headers
- Ktor Tutorial #4: Routing — REST endpoints
- Ktor Tutorial #5: Serialization — JSON handling
- Kotlin Tutorial: Complete Series — Learn Kotlin from scratch