or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconfiguration.mdengine.mdindex.mdrequest-response.mdrouting.md

routing.mddocs/

0

# Routing System

1

2

Ktor's routing system provides a flexible and powerful way to define URL patterns, handle HTTP methods, and organize application endpoints. The routing DSL allows you to create hierarchical route structures with parameter capturing, content negotiation, and middleware integration.

3

4

## Core Routing Classes

5

6

### RoutingNode

7

8

```kotlin { .api }

9

class RoutingNode(

10

parent: RoutingNode?,

11

selector: RouteSelector,

12

developmentMode: Boolean,

13

environment: ApplicationEnvironment

14

) : ApplicationCallPipeline, Route {

15

val parent: RoutingNode?

16

val selector: RouteSelector

17

val children: List<RoutingNode>

18

19

fun createChild(selector: RouteSelector): RoutingNode

20

fun handle(body: PipelineContext<Unit, PipelineCall>.() -> Unit)

21

operator fun invoke(body: Route.() -> Unit): Route

22

}

23

```

24

25

### Route Interface

26

27

```kotlin { .api }

28

interface Route : ApplicationCallPipeline {

29

val parent: Route?

30

val selector: RouteSelector

31

32

// Route building functions

33

fun createChild(selector: RouteSelector): Route

34

fun handle(handler: PipelineInterceptor<Unit, PipelineCall>)

35

}

36

```

37

38

## Basic Route Creation

39

40

### String Path Routing

41

42

```kotlin { .api }

43

// Route for exact string path

44

fun Route.route(path: String, build: Route.() -> Unit): Route

45

46

// Route for string path with specific HTTP method

47

fun Route.route(path: String, method: HttpMethod, build: Route.() -> Unit): Route

48

49

// Examples

50

routing {

51

route("/users") {

52

// Nested routes under /users

53

route("/profile") {

54

// Handles /users/profile

55

}

56

}

57

58

route("/api", HttpMethod.Get) {

59

// Only GET requests to /api/*

60

}

61

}

62

```

63

64

### Regex Path Routing

65

66

```kotlin { .api }

67

// Route for regex path pattern

68

fun Route.route(path: Regex, build: Route.() -> Unit): Route

69

70

// Example

71

routing {

72

route(Regex("/files/.*\\.(jpg|png|gif)")) {

73

// Matches image file paths

74

handle {

75

val path = call.request.path()

76

call.respondText("Image file: $path")

77

}

78

}

79

}

80

```

81

82

## HTTP Method Routes

83

84

### Standard HTTP Methods

85

86

```kotlin { .api }

87

// HTTP method route functions

88

fun Route.get(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

89

fun Route.post(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

90

fun Route.put(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

91

fun Route.delete(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

92

fun Route.patch(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

93

fun Route.head(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

94

fun Route.options(path: String = "", body: PipelineInterceptor<Unit, ApplicationCall>): Route

95

96

// Generic method route

97

fun Route.method(method: HttpMethod, body: Route.() -> Unit): Route

98

```

99

100

### HTTP Method Examples

101

102

```kotlin { .api }

103

routing {

104

// GET route

105

get("/") {

106

call.respondText("Welcome to Ktor!")

107

}

108

109

// POST route with path

110

post("/users") {

111

val user = call.receive<User>()

112

val created = userService.create(user)

113

call.respond(HttpStatusCode.Created, created)

114

}

115

116

// PUT route for updates

117

put("/users/{id}") {

118

val id = call.parameters["id"]!!

119

val user = call.receive<User>()

120

val updated = userService.update(id, user)

121

call.respond(updated)

122

}

123

124

// DELETE route

125

delete("/users/{id}") {

126

val id = call.parameters["id"]!!

127

userService.delete(id)

128

call.respond(HttpStatusCode.NoContent)

129

}

130

131

// Custom method handling

132

method(HttpMethod.Patch) {

133

route("/users/{id}") {

134

handle {

135

// Handle PATCH request

136

}

137

}

138

}

139

}

140

```

141

142

## Parameter Routing

143

144

### Path Parameters

145

146

```kotlin { .api }

147

// Route with parameter capture

148

get("/users/{id}") {

149

val userId = call.parameters["id"]

150

call.respondText("User ID: $userId")

151

}

152

153

// Multiple parameters

154

get("/users/{userId}/posts/{postId}") {

155

val userId = call.parameters["userId"]

156

val postId = call.parameters["postId"]

157

call.respond(getPost(userId, postId))

158

}

159

160

// Optional parameters with wildcards

161

get("/files/{path...}") {

162

val filePath = call.parameters["path"]

163

call.respondText("File path: $filePath")

164

}

165

```

166

167

### Query Parameters

168

169

```kotlin { .api }

170

// Parameter routing functions

171

fun Route.param(name: String, value: String, build: Route.() -> Unit): Route

172

fun Route.param(name: String, build: Route.() -> Unit): Route

173

fun Route.optionalParam(name: String, build: Route.() -> Unit): Route

174

175

// Examples

176

routing {

177

// Route only when parameter has specific value

178

param("version", "v1") {

179

get("/api/users") {

180

// Only matches /api/users?version=v1

181

}

182

}

183

184

// Route that captures any parameter value

185

param("format") {

186

get("/data") {

187

val format = call.parameters["format"]

188

// Matches /data?format=json, /data?format=xml, etc.

189

}

190

}

191

192

// Route for optional parameter

193

optionalParam("limit") {

194

get("/items") {

195

val limit = call.parameters["limit"]?.toIntOrNull() ?: 10

196

// Works with or without ?limit=N

197

}

198

}

199

}

200

```

201

202

### Parameter Access

203

204

```kotlin { .api }

205

get("/search") {

206

// Query parameters

207

val query = call.request.queryParameters["q"]

208

val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1

209

val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 20

210

211

// Path parameters

212

val category = call.parameters["category"]

213

214

val results = searchService.search(query, category, page, size)

215

call.respond(results)

216

}

217

```

218

219

## Header and Host Routing

220

221

### Header-Based Routing

222

223

```kotlin { .api }

224

// Route for specific header value

225

fun Route.header(name: String, value: String, build: Route.() -> Unit): Route

226

227

// Examples

228

routing {

229

header("Content-Type", "application/json") {

230

post("/api/data") {

231

// Only matches requests with JSON content type

232

val data = call.receive<JsonObject>()

233

call.respond(processJsonData(data))

234

}

235

}

236

237

header("Accept", "application/xml") {

238

get("/api/users") {

239

// Only matches requests that accept XML

240

val users = userService.getAllUsers()

241

call.respond(users.toXml())

242

}

243

}

244

245

header("Authorization") { // Any Authorization header

246

get("/protected") {

247

val token = call.request.header("Authorization")

248

// Handle authenticated requests

249

}

250

}

251

}

252

```

253

254

### Host and Port Routing

255

256

```kotlin { .api }

257

// Route for specific host

258

fun Route.host(host: String, build: Route.() -> Unit): Route

259

260

// Route for specific port

261

fun Route.port(port: Int, build: Route.() -> Unit): Route

262

263

// Examples

264

routing {

265

host("api.example.com") {

266

get("/v1/users") {

267

// Only matches requests to api.example.com

268

}

269

}

270

271

host("admin.example.com") {

272

get("/dashboard") {

273

// Admin interface on subdomain

274

}

275

}

276

277

port(8443) {

278

get("/secure") {

279

// Only matches requests on port 8443

280

}

281

}

282

}

283

```

284

285

## Route Selectors

286

287

### RouteSelector Classes

288

289

```kotlin { .api }

290

// Base route selector interface

291

abstract class RouteSelector {

292

abstract fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation

293

}

294

295

// Path segment selector

296

class PathSegmentRouteSelector(val value: String) : RouteSelector

297

298

// HTTP method selector

299

class HttpMethodRouteSelector(val method: HttpMethod) : RouteSelector

300

301

// Parameter selector

302

class ParameterRouteSelector(val name: String) : RouteSelector

303

304

// HTTP header selector

305

class HttpHeaderRouteSelector(val name: String, val value: String? = null) : RouteSelector

306

```

307

308

### Custom Route Selectors

309

310

```kotlin { .api }

311

// Create custom route selector

312

class CustomRouteSelector : RouteSelector() {

313

override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {

314

// Custom routing logic

315

return if (customCondition(context)) {

316

RouteSelectorEvaluation.Constant

317

} else {

318

RouteSelectorEvaluation.Failed

319

}

320

}

321

322

private fun customCondition(context: RoutingResolveContext): Boolean {

323

// Implement custom routing condition

324

return context.call.request.userAgent()?.contains("MyApp") == true

325

}

326

}

327

328

// Use custom selector

329

fun Route.customRoute(build: Route.() -> Unit): Route {

330

return createChild(CustomRouteSelector()).apply(build)

331

}

332

```

333

334

## Routing Pipeline Classes

335

336

### RoutingPipelineCall

337

338

```kotlin { .api }

339

class RoutingPipelineCall(

340

call: ApplicationCall,

341

route: Route,

342

receivePipeline: ApplicationReceivePipeline,

343

sendPipeline: ApplicationSendPipeline,

344

parameters: Parameters

345

) : PipelineCall {

346

val route: Route

347

// Additional routing-specific context

348

}

349

```

350

351

### RoutingResolveContext

352

353

```kotlin { .api }

354

class RoutingResolveContext(

355

val routing: RoutingNode,

356

val call: ApplicationCall,

357

val parameters: Parameters

358

) {

359

// Context for route resolution process

360

}

361

```

362

363

### RoutingPath

364

365

```kotlin { .api }

366

class RoutingPath(val parts: List<RoutingPathSegment>) {

367

companion object {

368

fun parse(path: String): RoutingPath

369

fun root(): RoutingPath

370

}

371

}

372

373

sealed class RoutingPathSegment {

374

object Root : RoutingPathSegment()

375

data class Constant(val value: String) : RoutingPathSegment()

376

data class Parameter(val name: String) : RoutingPathSegment()

377

}

378

```

379

380

## Advanced Routing Patterns

381

382

### Route Groups and Organization

383

384

```kotlin { .api }

385

routing {

386

// API versioning

387

route("/api") {

388

route("/v1") {

389

userRoutesV1()

390

productRoutesV1()

391

}

392

393

route("/v2") {

394

userRoutesV2()

395

productRoutesV2()

396

}

397

}

398

399

// Admin routes

400

route("/admin") {

401

authenticate("admin") {

402

userManagementRoutes()

403

systemRoutes()

404

}

405

}

406

407

// Public routes

408

staticRoutes()

409

authRoutes()

410

}

411

412

fun Route.userRoutesV1() {

413

route("/users") {

414

get {

415

call.respond(userService.getAllUsers())

416

}

417

418

post {

419

val user = call.receive<User>()

420

call.respond(userService.createUser(user))

421

}

422

423

route("/{id}") {

424

get {

425

val id = call.parameters["id"]!!

426

call.respond(userService.getUser(id))

427

}

428

429

put {

430

val id = call.parameters["id"]!!

431

val user = call.receive<User>()

432

call.respond(userService.updateUser(id, user))

433

}

434

435

delete {

436

val id = call.parameters["id"]!!

437

userService.deleteUser(id)

438

call.respond(HttpStatusCode.NoContent)

439

}

440

}

441

}

442

}

443

```

444

445

### Content Negotiation in Routes

446

447

```kotlin { .api }

448

routing {

449

route("/api/data") {

450

// JSON endpoint

451

header("Accept", "application/json") {

452

get {

453

call.respond(dataService.getData())

454

}

455

}

456

457

// XML endpoint

458

header("Accept", "application/xml") {

459

get {

460

call.respondText(

461

dataService.getData().toXml(),

462

ContentType.Application.Xml

463

)

464

}

465

}

466

467

// CSV endpoint

468

header("Accept", "text/csv") {

469

get {

470

call.respondText(

471

dataService.getData().toCsv(),

472

ContentType.Text.CSV

473

)

474

}

475

}

476

}

477

}

478

```

479

480

### Conditional Routing

481

482

```kotlin { .api }

483

routing {

484

// Development-only routes

485

if (developmentMode) {

486

route("/debug") {

487

get("/routes") {

488

val routes = collectRoutes(this@routing)

489

call.respond(routes)

490

}

491

492

get("/config") {

493

call.respond(application.environment.config.toMap())

494

}

495

}

496

}

497

498

// Feature-flagged routes

499

if (featureFlags.isEnabled("new-api")) {

500

route("/api/v3") {

501

newApiRoutes()

502

}

503

}

504

}

505

```

506

507

## Trailing Slash Configuration

508

509

### IgnoreTrailingSlash

510

511

```kotlin { .api }

512

// Configure trailing slash handling

513

install(IgnoreTrailingSlash)

514

515

// Now these routes are equivalent:

516

// GET /users

517

// GET /users/

518

519

routing {

520

get("/users") {

521

call.respondText("Users endpoint")

522

}

523

// This will handle both /users and /users/

524

}

525

```

526

527

## Route Information and Debugging

528

529

### Route Collection

530

531

```kotlin { .api }

532

fun collectRoutes(root: Route): List<RouteInfo> {

533

val routes = mutableListOf<RouteInfo>()

534

535

fun collectRoutesRecursive(route: Route, path: String = "") {

536

val currentPath = path + when (val selector = route.selector) {

537

is PathSegmentRouteSelector -> "/${selector.value}"

538

is ParameterRouteSelector -> "/{${selector.name}}"

539

is HttpMethodRouteSelector -> " [${selector.method.value}]"

540

else -> ""

541

}

542

543

if (route.children.isEmpty()) {

544

routes.add(RouteInfo(currentPath, route.selector))

545

}

546

547

route.children.forEach { child ->

548

collectRoutesRecursive(child, currentPath)

549

}

550

}

551

552

collectRoutesRecursive(root)

553

return routes

554

}

555

556

data class RouteInfo(val path: String, val selector: RouteSelector)

557

```

558

559

## Complete Routing Example

560

561

```kotlin { .api }

562

fun Application.configureRouting() {

563

routing {

564

// Root endpoint

565

get("/") {

566

call.respondText("Welcome to our API")

567

}

568

569

// API routes

570

route("/api/v1") {

571

// Content-Type based routing

572

header("Content-Type", "application/json") {

573

574

// User management

575

route("/users") {

576

get {

577

val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 0

578

val size = call.request.queryParameters["size"]?.toIntOrNull() ?: 20

579

call.respond(userService.getUsers(page, size))

580

}

581

582

post {

583

val user = call.receive<CreateUserRequest>()

584

val created = userService.createUser(user)

585

call.respond(HttpStatusCode.Created, created)

586

}

587

588

route("/{id}") {

589

get {

590

val id = call.parameters["id"]!!

591

val user = userService.getUser(id)

592

if (user != null) {

593

call.respond(user)

594

} else {

595

call.respond(HttpStatusCode.NotFound)

596

}

597

}

598

599

put {

600

val id = call.parameters["id"]!!

601

val updateRequest = call.receive<UpdateUserRequest>()

602

val updated = userService.updateUser(id, updateRequest)

603

call.respond(updated)

604

}

605

606

delete {

607

val id = call.parameters["id"]!!

608

userService.deleteUser(id)

609

call.respond(HttpStatusCode.NoContent)

610

}

611

612

// User posts

613

route("/posts") {

614

get {

615

val userId = call.parameters["id"]!!

616

call.respond(postService.getUserPosts(userId))

617

}

618

619

post {

620

val userId = call.parameters["id"]!!

621

val post = call.receive<CreatePostRequest>()

622

val created = postService.createPost(userId, post)

623

call.respond(HttpStatusCode.Created, created)

624

}

625

}

626

}

627

}

628

629

// Search endpoint with query parameters

630

get("/search") {

631

val query = call.request.queryParameters["q"]

632

?: return@get call.respond(HttpStatusCode.BadRequest, "Missing query parameter")

633

val type = call.request.queryParameters["type"] ?: "all"

634

val results = searchService.search(query, type)

635

call.respond(results)

636

}

637

}

638

}

639

640

// Health check (no content-type requirement)

641

get("/health") {

642

call.respondText("OK", ContentType.Text.Plain)

643

}

644

645

// Static files

646

route("/static/{path...}") {

647

get {

648

val path = call.parameters["path"]!!

649

val file = File("static/$path")

650

if (file.exists() && file.isFile) {

651

call.respondFile(file)

652

} else {

653

call.respond(HttpStatusCode.NotFound)

654

}

655

}

656

}

657

}

658

}

659

```

660

661

This comprehensive routing documentation covers all aspects of Ktor's routing system, from basic URL matching to advanced patterns and conditional routing strategies.