Skip to content

Commit 716da9f

Browse files
committed
docs: enhanced library details and usage examples
1 parent f2d4744 commit 716da9f

File tree

1 file changed

+67
-51
lines changed

1 file changed

+67
-51
lines changed

README.md

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,82 @@
1+
![GitHub release (with filter)](https://img.shields.io/github/v/release/y9vad9/rsocket-kotlin-router)
2+
![GitHub](https://img.shields.io/github/license/y9vad9/rsocket-kotlin-router)
13
# rsocket-kotlin-router
2-
34
`rsocket-kotlin-router` is a customisable library designed to streamline and simplify routing
45
for RSocket Kotlin server applications. This library offers a typesafe DSL for handling various
56
routes, serving as a declarative simplified alternative to manual routing that would
67
otherwise result in long-winded ternary logic or exhaustive when statements.
78

8-
## Motivation
9-
10-
In transitioning from gRPC to RSocket, a key challenge faced was managing requests
11-
safely and efficiently in the absence of a built-in routing system. Although RSocket
12-
provides experimental support for retrieving route metadata, it fails to offer bundled
13-
logic for declaring routes. This would typically result in a convoluted and unscaleable
14-
routing setup for larger projects. `rsocket-kotlin-router` serves as a solution to this,
15-
providing a neat and modular approach to managing RSocket routes and not only.
16-
17-
## Example
18-
Here is the small example of how it all works:
9+
## Features
10+
### Router
11+
It's the basic thing in the `rsocket-kotlin-router` that's responsible for managing routes, their settings, etc. You
12+
can define it in a next way:
1913
```kotlin
20-
internal fun RSocketConnectionAcceptor(
21-
service: YourService,
22-
): ConnectionAcceptor {
23-
return ConnectionAcceptor {
24-
RSocketRequestHandler {
25-
router {
26-
routeSeparator = '.'
27-
routeProvider { metadata -> metadata?.read(RoutingMetadata)?.tags?.first() ?: error("...") }
28-
29-
preprocessors {
30-
// executes before routing feature
31-
forCoroutineContext(MyRequestPreprocessor())
32-
}
33-
34-
sharedInterceptors {
35-
forCoroutineContext(MyCoroutineContextInterceptor())
36-
}
37-
38-
routing {
39-
route("users") {
40-
interceptors {
41-
// registers for current route and sub-routes.
42-
forModification(MyModificationInterceptor())
43-
}
44-
45-
requestResponse("get") { payload ->
46-
TODO()
47-
}
48-
}
49-
}
50-
}
14+
val ServerRouter = router {
15+
router {
16+
routeSeparator = '.'
17+
routeProvider { metadata ->
18+
metadata?.read(RoutingMetadata)?.tags?.first()
19+
?: throw RSocketError.Invalid("No routing metadata was provided")
20+
}
21+
22+
routing { // this: RoutingBuilder
23+
// ...
5124
}
5225
}
5326
}
5427
```
28+
To install it later, you can use `Router.installOn(RSocketRequestHandlerBuilder)` function:
29+
```kotlin
30+
fun ServerRequestHandler(router: Router): RSocket = RSocketRequestHandlerBuilder {
31+
router.installOn(this)
32+
}
33+
```
34+
Or you can call `router` function directly in the `RSocketRequestHandlerBuilder` context – it will automatically
35+
install router on the given context.
5536

56-
So, as you can see, library provides `router` function for `RSocketRequestHandler` context. It has
57-
a few customization settings for handling routing in your own style. In addition to it, for convenience, library also provides its own interceptors API (for processing `Payload`) with
58-
kotlin coroutines and modifications. You can take a look at them [here](router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt).
37+
### Routing Builder
38+
You can define routes using bundled DSL-Builder functions:
39+
```kotlin
40+
fun RoutingBuilder.usersRoute(): Unit = route("users") {
41+
// extension function that wraps RSocket `requestResponse` into `route` with given path.
42+
requestResponse("get") { payload -> TODO() }
5943

60-
### Testing
44+
// ... other
45+
}
46+
```
47+
> **Note** <br>
48+
> The library does not include the functionality to add routing to a `metadataPush` type of request. I am not sure
49+
> how it should be exactly implemented (API), so your ideas are welcome. For now, I consider it as per-project responsibility.
50+
### Interceptors
51+
> **Warning** <br>
52+
> Interceptors are experimental feature: API can be changed in the future.
6153
62-
`rsocket-kotlin-router` provides ability to test your routes with `router-test` artifact:
54+
#### Preprocessors
55+
Preprocessors are utilities that run before routing feature applies. For cases, when you need to transform input into something or propagate
56+
values using coroutines – you can extend [`Preprocessor.Modifier`](https://github.com/y9vad9/rsocket-kotlin-router/blob/2a794e9a8c5d2ac53cb87ea58cfbe4a2ecfa217d/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L39) or [`Preprocessor.CoroutineContext`](https://github.com/y9vad9/rsocket-kotlin-router/blob/master/router-core/src/commonMain/kotlin/com.y9vad9.rsocket.router/interceptors/Interceptor.kt#L31). Here's an example:
57+
```kotlin
58+
class MyCoroutineContextElement(val value: String): CoroutineContext.Element {...}
59+
60+
@OptIn(ExperimentalInterceptorsApi::class)
61+
class MyCoroutineContextPreprocessor : Preprocessor.CoroutineContext {
62+
override fun intercept(coroutineContext: CoroutineContext, input: Payload): CoroutineContext {
63+
return coroutineContext + MyCoroutineContextElement(value = "smth")
64+
}
65+
}
66+
```
6367

68+
#### Route Interceptors
69+
In addition to the `Preprocessors`, `rsocket-kotlin-router` also provides API to intercept specific routes:
70+
```kotlin
71+
@OptIn(ExperimentalInterceptorsApi::class)
72+
class MyRouteInterceptor : RouteInterceptor.Modifier {
73+
override fun intercept(route: String, input: Payload): Payload {
74+
return Payload.Empty // just for example
75+
}
76+
}
77+
```
78+
### Testability
79+
`rsocket-kotlin-router` provides ability to test your routes with `router-test` artifact:
6480
```kotlin
6581
@Test
6682
fun testRoutes() {
@@ -71,7 +87,7 @@ fun testRoutes() {
7187
route1.assertHasInterceptor<MyInterceptor>()
7288
route2.assertHasInterceptor<MyInterceptor>()
7389

74-
route2.fireAndForgetOrAssert(emptyRSocket(), buildPayload {
90+
route2.fireAndForgetOrAssert(buildPayload {
7591
data("test")
7692
})
7793
}
@@ -87,9 +103,9 @@ repositories {
87103
}
88104

89105
dependencies {
90-
implementation("com.y9vad9.rsocket.router:router-core:1.1.0")
106+
implementation("com.y9vad9.rsocket.router:router-core:$version")
91107
// for testing
92-
implementation("com.y9vad9.rsocket.router:router-test:1.1.0")
108+
implementation("com.y9vad9.rsocket.router:router-test:$version")
93109
}
94110
```
95111
> For now, it's available for JVM only, but as there is no JVM platform API used,

0 commit comments

Comments
 (0)