or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconfig.mdengine.mdindex.mdrequest-response.mdrouting.mdutilities.md

routing.mddocs/

0

# Routing System

1

2

Powerful routing DSL with pattern matching, parameter extraction, and nested route organization. Provides declarative route definition with support for HTTP methods, path patterns, and conditional routing based on headers, parameters, and other request characteristics.

3

4

## Capabilities

5

6

### Core Routing Interfaces

7

8

Base interfaces for route definition and organization.

9

10

```kotlin { .api }

11

/**

12

* Represents a route node in routing tree

13

*/

14

interface Route {

15

/** Parent route in the hierarchy */

16

val parent: Route?

17

18

/** Selector used to match this route */

19

val selector: RouteSelector

20

21

/** Whether development mode is enabled */

22

val developmentMode: Boolean

23

24

/** Application environment */

25

val environment: ApplicationEnvironment

26

}

27

28

/**

29

* Implementation of route with child routes and handlers

30

*/

31

open class RoutingNode : Route {

32

/** Child routes */

33

val children: List<RoutingNode>

34

35

/** List of handlers for this route */

36

val handlers: List<PipelineInterceptor<Unit, PipelineCall>>

37

38

/** Create child route with given selector */

39

fun createChild(selector: RouteSelector): RoutingNode

40

}

41

42

/**

43

* Root routing node for an application

44

*/

45

class Routing : RoutingNode

46

```

47

48

### Routing Builder DSL

49

50

DSL interface for constructing routes declaratively.

51

52

```kotlin { .api }

53

/**

54

* DSL builder interface for constructing routes

55

*/

56

interface RoutingBuilder {

57

/** Create child route with path pattern */

58

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

59

60

/** Create route for specific HTTP method */

61

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

62

63

/** Create route matching parameter value */

64

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

65

66

/** Create route matching optional parameter */

67

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

68

69

/** Create route matching header value */

70

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

71

72

/** Create route matching Accept header */

73

fun accept(contentType: ContentType, build: Route.() -> Unit): Route

74

75

/** Create route matching Content-Type header */

76

fun contentType(contentType: ContentType, build: Route.() -> Unit): Route

77

78

/** Create route matching host pattern */

79

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

80

81

/** Create route matching port number */

82

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

83

84

/** Add handler to current route */

85

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

86

}

87

```

88

89

### HTTP Method DSL Functions

90

91

Convenient DSL functions for common HTTP methods.

92

93

```kotlin { .api }

94

/** Handle GET requests */

95

fun Route.get(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

96

97

/** Handle POST requests */

98

fun Route.post(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

99

100

/** Handle PUT requests */

101

fun Route.put(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

102

103

/** Handle DELETE requests */

104

fun Route.delete(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

105

106

/** Handle PATCH requests */

107

fun Route.patch(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

108

109

/** Handle HEAD requests */

110

fun Route.head(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

111

112

/** Handle OPTIONS requests */

113

fun Route.options(path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

114

115

/** Handle specific HTTP method */

116

fun Route.method(method: HttpMethod, path: String = "", body: suspend PipelineContext<Unit, PipelineCall>.() -> Unit): Route

117

```

118

119

### Route Selectors

120

121

Classes that define how routes are matched against incoming requests.

122

123

#### Base Route Selector

124

125

```kotlin { .api }

126

/**

127

* Base class for route selection criteria

128

*/

129

abstract class RouteSelector {

130

/** Evaluate if this selector matches the request */

131

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

132

133

/** Quality score for route selection priority */

134

open val quality: Double

135

}

136

```

137

138

#### Path Segment Selectors

139

140

```kotlin { .api }

141

/**

142

* Matches exact path segment

143

*/

144

data class PathSegmentConstantRouteSelector(val value: String) : RouteSelector

145

146

/**

147

* Matches path segment parameter and captures value

148

*/

149

data class PathSegmentParameterRouteSelector(

150

val name: String,

151

val prefix: String? = null,

152

val suffix: String? = null

153

) : RouteSelector

154

155

/**

156

* Matches optional path segment parameter

157

*/

158

data class PathSegmentOptionalParameterRouteSelector(

159

val name: String,

160

val prefix: String? = null,

161

val suffix: String? = null

162

) : RouteSelector

163

164

/**

165

* Matches any single path segment

166

*/

167

object PathSegmentWildcardRouteSelector : RouteSelector

168

169

/**

170

* Matches remaining path segments (catch-all)

171

*/

172

object PathSegmentTailcardRouteSelector : RouteSelector

173

```

174

175

#### HTTP-Based Selectors

176

177

```kotlin { .api }

178

/**

179

* Matches HTTP method

180

*/

181

data class HttpMethodRouteSelector(val method: HttpMethod) : RouteSelector

182

183

/**

184

* Matches HTTP header value

185

*/

186

data class HttpHeaderRouteSelector(

187

val name: String,

188

val value: String

189

) : RouteSelector

190

191

/**

192

* Matches Accept header content type

193

*/

194

data class HttpAcceptRouteSelector(val contentType: ContentType) : RouteSelector

195

```

196

197

#### Connection-Based Selectors

198

199

```kotlin { .api }

200

/**

201

* Matches connection port

202

*/

203

data class LocalPortRouteSelector(val port: Int) : RouteSelector

204

205

/**

206

* Matches host pattern (supports wildcards)

207

*/

208

class HostRouteSelector(val hostPattern: String) : RouteSelector

209

```

210

211

### Route Selection Evaluation

212

213

Classes representing the result of route selector evaluation.

214

215

```kotlin { .api }

216

/**

217

* Result of route selector evaluation

218

*/

219

sealed class RouteSelectorEvaluation(val succeeded: Boolean) {

220

/**

221

* Successful match with quality score and extracted parameters

222

*/

223

data class Success(

224

val quality: Double,

225

val parameters: Parameters = Parameters.Empty,

226

val segmentIncrement: Int = 0

227

) : RouteSelectorEvaluation(true)

228

229

/**

230

* Failed match with quality score and failure status

231

*/

232

data class Failure(

233

val quality: Double,

234

val failureStatusCode: HttpStatusCode

235

) : RouteSelectorEvaluation(false)

236

237

companion object {

238

// Quality constants

239

const val qualityConstant: Double = 1.0

240

const val qualityQueryParameter: Double = 1.0

241

const val qualityParameterWithPrefixOrSuffix: Double = 0.9

242

const val qualityParameter: Double = 0.8

243

const val qualityPathParameter: Double = qualityParameter

244

const val qualityWildcard: Double = 0.5

245

const val qualityMissing: Double = 0.2

246

const val qualityTailcard: Double = 0.1

247

const val qualityTransparent: Double = -1.0

248

const val qualityFailedMethod: Double = 0.02

249

const val qualityFailedParameter: Double = 0.01

250

251

// Common evaluation results

252

val Failed: RouteSelectorEvaluation.Failure

253

val FailedPath: RouteSelectorEvaluation.Failure

254

val FailedMethod: RouteSelectorEvaluation.Failure

255

val FailedParameter: RouteSelectorEvaluation.Failure

256

val FailedAcceptHeader: RouteSelectorEvaluation.Failure

257

val Missing: RouteSelectorEvaluation

258

val Constant: RouteSelectorEvaluation

259

val Transparent: RouteSelectorEvaluation

260

val ConstantPath: RouteSelectorEvaluation

261

val WildcardPath: RouteSelectorEvaluation

262

}

263

}

264

```

265

266

### Route Resolution

267

268

Classes for resolving routes and handling the resolution process.

269

270

```kotlin { .api }

271

/**

272

* Context for route resolution process

273

*/

274

class RoutingResolveContext {

275

/** Root routing node */

276

val routing: RoutingNode

277

278

/** Current application call */

279

val call: RoutingApplicationCall

280

281

/** List of resolution tracers for debugging */

282

val tracers: List<(RoutingResolveTrace) -> Unit>

283

284

/** Captured route parameters */

285

val parameters: Parameters

286

287

/** Request headers */

288

val headers: Headers

289

}

290

291

/**

292

* Result of route resolution

293

*/

294

data class RoutingResolveResult(

295

/** Matched route */

296

val route: RoutingNode,

297

298

/** Captured parameters */

299

val parameters: Parameters,

300

301

/** Quality score of match */

302

val quality: Double

303

)

304

```

305

306

### Routing Installation

307

308

Function to install routing into an application.

309

310

```kotlin { .api }

311

/**

312

* Install and configure routing for the application

313

*/

314

fun Application.routing(configuration: Routing.() -> Unit): Routing

315

```

316

317

### Route Introspection

318

319

Extensions for examining and debugging route structures.

320

321

```kotlin { .api }

322

/** Get all descendant routes recursively */

323

val RoutingNode.allRoutes: List<RoutingNode>

324

325

/** Get all routes recursively with filtering */

326

fun RoutingNode.getAllRoutes(predicate: (RoutingNode) -> Boolean = { true }): List<RoutingNode>

327

328

/** Create route from path pattern */

329

fun createRouteFromPath(path: String): List<RouteSelector>

330

```

331

332

## Usage Examples

333

334

### Basic Routing Setup

335

336

```kotlin

337

import io.ktor.server.application.*

338

import io.ktor.server.response.*

339

import io.ktor.server.routing.*

340

341

fun Application.basicRouting() {

342

routing {

343

// Simple GET route

344

get("/") {

345

call.respondText("Hello, World!")

346

}

347

348

// Route with path parameter

349

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

350

val id = call.parameters["id"]

351

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

352

}

353

354

// Multiple HTTP methods

355

route("/api/users") {

356

get {

357

call.respondText("Get all users")

358

}

359

360

post {

361

call.respondText("Create user")

362

}

363

364

route("/{id}") {

365

get {

366

val id = call.parameters["id"]

367

call.respondText("Get user $id")

368

}

369

370

put {

371

val id = call.parameters["id"]

372

call.respondText("Update user $id")

373

}

374

375

delete {

376

val id = call.parameters["id"]

377

call.respondText("Delete user $id")

378

}

379

}

380

}

381

}

382

}

383

```

384

385

### Advanced Path Patterns

386

387

```kotlin

388

import io.ktor.server.application.*

389

import io.ktor.server.response.*

390

import io.ktor.server.routing.*

391

392

fun Application.advancedPatterns() {

393

routing {

394

// Optional parameter

395

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

396

val id = call.parameters["id"]

397

if (id != null) {

398

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

399

} else {

400

call.respondText("All users")

401

}

402

}

403

404

// Parameter with prefix/suffix

405

get("/files/{name}.{ext}") {

406

val name = call.parameters["name"]

407

val ext = call.parameters["ext"]

408

call.respondText("File: $name.$ext")

409

}

410

411

// Wildcard segment

412

get("/static/*") {

413

val path = call.parameters.getAll("*")?.joinToString("/")

414

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

415

}

416

417

// Tailcard (catch remaining segments)

418

get("/docs/{...}") {

419

val pathSegments = call.parameters.getAll("...")

420

val docPath = pathSegments?.joinToString("/") ?: ""

421

call.respondText("Documentation: $docPath")

422

}

423

424

// Multiple parameters

425

get("/api/v{version}/users/{userId}/posts/{postId}") {

426

val version = call.parameters["version"]

427

val userId = call.parameters["userId"]

428

val postId = call.parameters["postId"]

429

call.respondText("API v$version: User $userId, Post $postId")

430

}

431

}

432

}

433

```

434

435

### Conditional Routing

436

437

```kotlin

438

import io.ktor.server.application.*

439

import io.ktor.server.response.*

440

import io.ktor.server.routing.*

441

442

fun Application.conditionalRouting() {

443

routing {

444

// Route based on header

445

route("/api") {

446

header("X-API-Version", "2") {

447

get("/users") {

448

call.respondText("API v2: Users")

449

}

450

}

451

452

header("X-API-Version", "1") {

453

get("/users") {

454

call.respondText("API v1: Users")

455

}

456

}

457

}

458

459

// Route based on Accept header

460

route("/data") {

461

accept(ContentType.Application.Json) {

462

get {

463

call.respond(mapOf("data" to "json"))

464

}

465

}

466

467

accept(ContentType.Application.Xml) {

468

get {

469

call.respondText("<data>xml</data>", ContentType.Application.Xml)

470

}

471

}

472

}

473

474

// Route based on Content-Type

475

route("/upload") {

476

contentType(ContentType.Application.Json) {

477

post {

478

call.respondText("JSON upload")

479

}

480

}

481

482

contentType(ContentType.MultiPart.FormData) {

483

post {

484

call.respondText("Multipart upload")

485

}

486

}

487

}

488

489

// Route based on host

490

host("api.example.com") {

491

get("/") {

492

call.respondText("API subdomain")

493

}

494

}

495

496

host("admin.example.com") {

497

get("/") {

498

call.respondText("Admin subdomain")

499

}

500

}

501

502

// Route based on port

503

port(8080) {

504

get("/dev") {

505

call.respondText("Development server")

506

}

507

}

508

509

port(443) {

510

get("/secure") {

511

call.respondText("Secure connection")

512

}

513

}

514

}

515

}

516

```

517

518

### Nested Route Organization

519

520

```kotlin

521

import io.ktor.server.application.*

522

import io.ktor.server.response.*

523

import io.ktor.server.routing.*

524

525

fun Application.nestedRouting() {

526

routing {

527

// API versioning

528

route("/api") {

529

route("/v1") {

530

route("/users") {

531

get {

532

call.respondText("V1: Get users")

533

}

534

535

post {

536

call.respondText("V1: Create user")

537

}

538

539

route("/{id}") {

540

get {

541

call.respondText("V1: Get user ${call.parameters["id"]}")

542

}

543

544

route("/posts") {

545

get {

546

call.respondText("V1: Get user posts")

547

}

548

}

549

}

550

}

551

}

552

553

route("/v2") {

554

route("/users") {

555

get {

556

call.respondText("V2: Get users")

557

}

558

559

route("/{id}") {

560

get {

561

call.respondText("V2: Get user ${call.parameters["id"]}")

562

}

563

}

564

}

565

}

566

}

567

568

// Admin section

569

route("/admin") {

570

// Apply authentication here

571

route("/users") {

572

get {

573

call.respondText("Admin: List users")

574

}

575

576

delete("/{id}") {

577

call.respondText("Admin: Delete user ${call.parameters["id"]}")

578

}

579

}

580

581

route("/settings") {

582

get {

583

call.respondText("Admin: Settings")

584

}

585

586

post {

587

call.respondText("Admin: Update settings")

588

}

589

}

590

}

591

}

592

}

593

```

594

595

### Route Parameters and Query Strings

596

597

```kotlin

598

import io.ktor.server.application.*

599

import io.ktor.server.response.*

600

import io.ktor.server.routing.*

601

602

fun Application.parameterHandling() {

603

routing {

604

get("/search") {

605

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

606

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

607

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

608

val sort = call.request.queryParameters["sort"] ?: "relevance"

609

610

call.respond(mapOf(

611

"query" to query,

612

"page" to page,

613

"limit" to limit,

614

"sort" to sort,

615

"results" to emptyList<String>() // Mock results

616

))

617

}

618

619

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

620

val userId = call.parameters["id"]

621

val category = call.request.queryParameters["category"]

622

val published = call.request.queryParameters["published"]?.toBoolean()

623

624

val filters = mutableMapOf<String, Any>()

625

if (category != null) filters["category"] = category

626

if (published != null) filters["published"] = published

627

628

call.respond(mapOf(

629

"userId" to userId,

630

"filters" to filters,

631

"posts" to emptyList<String>()

632

))

633

}

634

635

// Multiple parameter validation

636

get("/date/{year}/{month}/{day}") {

637

val year = call.parameters["year"]?.toIntOrNull()

638

val month = call.parameters["month"]?.toIntOrNull()

639

val day = call.parameters["day"]?.toIntOrNull()

640

641

if (year == null || month == null || day == null) {

642

call.respond(HttpStatusCode.BadRequest, "Invalid date format")

643

return@get

644

}

645

646

if (month !in 1..12 || day !in 1..31) {

647

call.respond(HttpStatusCode.BadRequest, "Invalid date values")

648

return@get

649

}

650

651

call.respondText("Date: $year-$month-$day")

652

}

653

}

654

}

655

```

656

657

### Route Introspection and Debugging

658

659

```kotlin

660

import io.ktor.server.application.*

661

import io.ktor.server.response.*

662

import io.ktor.server.routing.*

663

664

fun Application.routeIntrospection() {

665

routing {

666

get("/debug/routes") {

667

val rootRoute = this@routing

668

val allRoutes = rootRoute.getAllRoutes()

669

670

val routeInfo = allRoutes.map { route ->

671

mapOf(

672

"selector" to route.selector.toString(),

673

"hasHandlers" to route.handlers.isNotEmpty(),

674

"childCount" to route.children.size

675

)

676

}

677

678

call.respond(mapOf(

679

"totalRoutes" to allRoutes.size,

680

"routes" to routeInfo

681

))

682

}

683

684

// Route that shows current call information

685

get("/debug/call") {

686

call.respond(mapOf(

687

"method" to call.request.httpMethod.value,

688

"uri" to call.request.uri,

689

"parameters" to call.parameters.toMap(),

690

"headers" to call.request.headers.toMap()

691

))

692

}

693

}

694

}

695

```

696

697

### Custom Route Selectors

698

699

```kotlin

700

import io.ktor.server.routing.*

701

702

// Custom selector that matches based on user agent

703

class UserAgentRouteSelector(private val userAgent: String) : RouteSelector() {

704

override val quality: Double = 0.8

705

706

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

707

val requestUserAgent = context.headers["User-Agent"]

708

return if (requestUserAgent?.contains(userAgent, ignoreCase = true) == true) {

709

RouteSelectorEvaluation.Constant(quality)

710

} else {

711

RouteSelectorEvaluation.Failed

712

}

713

}

714

715

override fun toString(): String = "UserAgent($userAgent)"

716

}

717

718

// Extension function to use custom selector

719

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

720

val selector = UserAgentRouteSelector(userAgent)

721

return createChild(selector).apply(build)

722

}

723

724

fun Application.customSelectors() {

725

routing {

726

userAgent("Mobile") {

727

get("/") {

728

call.respondText("Mobile version")

729

}

730

}

731

732

userAgent("Desktop") {

733

get("/") {

734

call.respondText("Desktop version")

735

}

736

}

737

738

// Default fallback

739

get("/") {

740

call.respondText("Standard version")

741

}

742

}

743

}

744

```