Tags¶
Organize your API endpoints into logical groups.
@Tag Annotation¶
Use the @Tag annotation to group related endpoints:
Kotlin
import io.github.tabilzad.ktor.annotations.Tag
@Tag(["Users"])
fun Route.userRoutes() {
route("/users") {
get { /* Listed under "Users" tag */ }
post { /* Listed under "Users" tag */ }
}
}
@Tag(["Products"])
fun Route.productRoutes() {
route("/products") {
get { /* Listed under "Products" tag */ }
post { /* Listed under "Products" tag */ }
}
}
Generated OpenAPI:
YAML
paths:
/users:
get:
tags:
- Users
post:
tags:
- Users
/products:
get:
tags:
- Products
post:
tags:
- Products
Multiple Tags¶
An endpoint can belong to multiple tags:
Kotlin
@Tag(["Users", "Admin"])
fun Route.adminUserRoutes() {
route("/admin/users") {
get { /* Listed under both "Users" and "Admin" */ }
}
}
Tag Inheritance¶
Tags are inherited by nested routes:
Kotlin
@Tag(["Orders"])
fun Route.orderRoutes() {
route("/orders") {
get { /* Tagged: Orders */ }
post { /* Tagged: Orders */ }
route("/{orderId}") {
get { /* Tagged: Orders */ }
put { /* Tagged: Orders */ }
delete { /* Tagged: Orders */ }
route("/items") {
get { /* Tagged: Orders */ }
post { /* Tagged: Orders */ }
}
}
}
}
Overriding Tags¶
You can override inherited tags on specific endpoints:
Kotlin
@Tag(["Orders"])
fun Route.orderRoutes() {
route("/orders") {
get { /* Tagged: Orders */ }
@Tag(["Order Items"])
route("/{orderId}/items") {
get { /* Tagged: Order Items (overrides Orders) */ }
}
}
}
Tag Naming Conventions¶
Good Tag Names¶
- Resource-based:
Users,Products,Orders - Feature-based:
Authentication,Notifications,Search - Version-based:
v1,v2(use sparingly) - Access-level:
Public,Admin,Internal
Examples¶
Kotlin
// Resource-based tags
@Tag(["Users"])
fun Route.userRoutes() { }
@Tag(["Products"])
fun Route.productRoutes() { }
@Tag(["Orders"])
fun Route.orderRoutes() { }
// Feature-based tags
@Tag(["Authentication"])
fun Route.authRoutes() { }
@Tag(["Search"])
fun Route.searchRoutes() { }
// Combined approach
@Tag(["Users", "Admin"])
fun Route.adminUserRoutes() { }
Organizing a Large API¶
By Resource¶
Kotlin
@GenerateOpenApi
fun Application.module() {
routing {
// Each resource gets its own tag
userRoutes() // @Tag(["Users"])
productRoutes() // @Tag(["Products"])
orderRoutes() // @Tag(["Orders"])
reviewRoutes() // @Tag(["Reviews"])
}
}
By Feature Area¶
Kotlin
@GenerateOpenApi
fun Application.module() {
routing {
// Public endpoints
publicRoutes() // @Tag(["Public"])
authenticate {
// Customer-facing
customerRoutes() // @Tag(["Customer"])
// Admin-only
adminRoutes() // @Tag(["Admin"])
}
}
}
By API Version¶
Kotlin
@GenerateOpenApi
fun Application.module() {
routing {
route("/api/v1") {
@Tag(["v1 - Users"])
route("/users") { }
@Tag(["v1 - Products"])
route("/products") { }
}
route("/api/v2") {
@Tag(["v2 - Users"])
route("/users") { }
@Tag(["v2 - Products"])
route("/products") { }
}
}
}
Tags in Swagger UI¶
Tags control how endpoints are grouped in Swagger UI:
Text Only
├── Users
│ ├── GET /users
│ ├── POST /users
│ ├── GET /users/{id}
│ ├── PUT /users/{id}
│ └── DELETE /users/{id}
├── Products
│ ├── GET /products
│ ├── POST /products
│ └── ...
└── Orders
├── GET /orders
└── ...
Combining with Descriptions¶
Use tags alongside descriptions for clear documentation:
Kotlin
@Tag(["Users"])
fun Route.userRoutes() {
route("/users") {
@KtorDescription(
summary = "List all users",
description = "Returns a paginated list of all users"
)
get {
responds<List<User>>(HttpStatusCode.OK)
}
@KtorDescription(
summary = "Create a new user",
description = "Creates a user account and sends welcome email"
)
post {
responds<User>(HttpStatusCode.Created)
val request = call.receive<CreateUserRequest>()
}
}
}
Best Practices¶
- Use consistent naming - Pick a convention and stick to it
- Keep tags high-level - Don't create too many tags
- Group logically - Tags should represent logical API areas
- Consider your audience - Name tags from the API consumer's perspective
- Avoid duplication - Each endpoint should typically have 1-2 tags
Recommended Tag Count¶
| API Size | Recommended Tags |
|---|---|
| Small (< 20 endpoints) | 3-5 tags |
| Medium (20-50 endpoints) | 5-10 tags |
| Large (50+ endpoints) | 10-15 tags |
Too many tags make navigation difficult. Too few provide inadequate organization.