Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2
Set of alternative routing implementations for Javalin 5.x & 6.x
License
javalin/javalin-routing-extensions
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Javalin is very flexible and allows you to extend it in many ways.This repository contains a set of extensions for Javalin routing system following some of the most popular patterns.Each approach has pros and cons, so you should choose the one that fits your needs the best.
Each module is distributed as a separate artifact:
dependencies {val javalinRoutingExtensions="6.7.0" implementation("io.javalin.community.routing:routing-core:$javalinRoutingExtensions") implementation("io.javalin.community.routing:routing-annotated:$javalinRoutingExtensions") implementation("io.javalin.community.routing:routing-dsl:$javalinRoutingExtensions") implementation("io.javalin.community.routing:routing-coroutines:$javalinRoutingExtensions")}This chapter provides short preview of each module.For more details, please refer to the documentation or full example of each module.First of all, not each module is available for Java users, take a look on the table below to check requirements:
| Module | Languages | Reflections |
|---|---|---|
| Annotated | Java, Kotlin | Yes(as long as we won't provide annotation processor) |
| DSL In-place DSL Properties | Kotlin | Optional |
| Coroutines | Kotlin | No |
| Core | Java, Kotlin | No |
This module provides set of annotations to simplify routing setup & basic http operations.This is probably the most common approach to routing in Java world,some people may even say that it's the only one.Take a look on the example below to see how it looks like:
importstaticio.javalin.community.routing.annotations.AnnotatedRouting.Annotated;// register endpoints with prefix@Endpoints("/api")staticfinalclassExampleEndpoints {privatefinalExampleServiceexampleService;// pass dependencies required to handle requestspublicExampleEndpoints(ExampleServiceexampleService) {this.exampleService =exampleService; }// use Javalin-specific routes@BeforevoidbeforeEach(Contextctx) {ctx.header("X-Example","Example"); }// describe http method and path with annotation@Post("/hello")// use parameters to extract data from requestvoidsaveExample(Contextcontext,@Nullable@Header(AUTHORIZATION)Stringauthorization,@BodyExampleDtoentity) {if (authorization ==null) {context.status(401);return; }exampleService.saveExample(entity); }// you can combine it with OpenApi plugin@OpenApi(path ="/api/hello/{name}",methods = {GET },summary ="Find example by name",pathParams = {@OpenApiParam(name ="name",description ="Name of example to find") },responses = {@OpenApiResponse(status ="200",description ="Example found",content =@OpenApiContent(from =ExampleDto.class)) },versions = {"default","2" } )// you can also use out-of-the-box support for versioned routes@Version("2")@Get("/hello/{name}")voidfindExampleV2(Contextcontext,@ParamStringname) {context.result(exampleService.findExampleByName(name).name); }/* OpenApi [...] */@Version("1")@Get("/hello/{name}")voidfindExampleV1(Contextctx) {thrownewUnsupportedOperationException("Deprecated"); }// register exception handlers alongside endpoints@ExceptionHandler(Exception.class)voiddefaultExceptionHandler(Exceptione,Contextctx) {ctx.status(500).result("Something went wrong: " +e.getClass()); }}publicstaticvoidmain(String[]args) {Javalin.createAndStart(config -> {// prepare dependenciesvarexampleService =newExampleService();// register endpointsconfig.router.mount(Annotated,routing -> {routing.registerEndpoints(newExampleEndpoints(exampleService)); }); });// test request to `saveExample` endpointHttpResponse<?>saved =Unirest.post("http://localhost:7000/api/hello") .basicAuth("Panda","passwd") .body(newExampleDto("Panda")) .asEmpty();System.out.println("Entity saved: " +saved.getStatusText());// Entity saved: OK// test request to `findExampleV2` endpointStringresult =Unirest.get("http://localhost:7000/api/hello/Panda") .header("X-API-Version","2") .asString() .getBody();System.out.println("Entity: " +result);// Entity: Panda}
This approach requires some reflections under the hood to make work at this moment,butwe're working on annotation processor to remove this requirement.
Another thing you can notice is that we're creating endpoint class instance using constructor,not built-in DI framework.This is because in general we consider this as a good practice -not only because you're in full control over the execution flow,but also because it forces you to make concious decision about the scope of your dependencies & architecture.
If you don't really care about it, and you're just looking for a tool that will get the job done,you can use literally any DI framework you want that is available for Java/Kotlin.We may recommend Dagger2, because it verifies your code at compile time,so it's safer than heavy reflection-based alternatives.
Keep in mind, that if you want to use named parameters in your endpoints,you have to pass-parameters flag to your compiler to preserve parameter names in bytecode.
Full example:AnnotationsRoutingExample.java
DSL provides extensible base for creating custom DSLs for routing.
By default,this module provides basic implementation of Ktor-like routing for Kotlin appswith support for type-safe paths:
@Path("/panda/{age}")data classPandaPath(valage:Int)funmain() {Javalin.create { config-> config.routing(CustomDsl) { before {// `endpointHandlerPath` comes from Context class result("Called endpoint:${matchedPath()}") } get("/") {// `helloWorld` comes from CustomScope class result(helloWorld()) } get<PandaPath> { path->// support for type-safe paths result(path.age.toString()) } exception(Exception::class) { anyException->// support for exception handlers result(anyException.message?:"Unknown error") } } }.start(8080)}
Because of the extensible nature of DSL, you may adjust it to your needs!You can find base implementation of custom DSL definition here:InPlaceExample.kt
Property based implementation of DSL allows you to easily define routes in multiple sources,outside the main setup scope.This approach is very similar to Spring Boot's@RestController annotation,but it also supports your custom type-safe DSL, andyou're in full control of your execution flow.
// Some dependenciesclassExampleService {funsave(animal:String)=println("Saved animal:$animal")}// Utility representation of custom routing in your applicationabstractclassExampleRouting :DslRoutes<DslRoute<CustomScope,Unit>,CustomScope,Unit>// Endpoint (domain router)classAnimalEndpoints(privatevalexampleService:ExampleService) : ExampleRouting() { @OpenApi( path="/animal/{name}", methods= [HttpMethod.GET] )privateval findAnimalByName= route("/animal/<name>",GET) { result(pathParam("name")) } @OpenApi( path="/animal/{name}", methods= [HttpMethod.POST] )privateval saveAnimal= route("/animal/<name>",POST) { exampleService.save(pathParam("name")) }privateval defaultExceptionHandler= exceptionHandler(Exception::class) { regularException->println("Exception:${regularException.message}") }overridefunroutes()=setOf(findAnimalByName, saveAnimal)overridefunexceptionHandlers()=setOf(defaultExceptionHandler)}funmain() {// prepare dependenciesval exampleService=ExampleService()// setup & launch applicationJavalin .create { it.routing(CustomDsl,AnimalEndpoints(exampleService)/*, provide more classes with endpoints*/) } .start(8080)}
This example is based on previous in-place example,you can check its source code here:PropertyDslExample.kt
Experimental: This module is more like a reference on how to use coroutines with Javalin.The production-readiness of this module is unknown, especially in complex scenarios.
The coroutines module provides API similar toDSL :: Properties module,but it uses coroutines & suspend directives to provide asynchronous & non-blocking endpoint execution.
// Custom scope used by routing DSLclassCustomScope(valctx:Context) : Context by ctx {// blocks thread using reactive `delay` functionsuspendfunnonBlockingDelay(message:String):String= delay(2000L).let { message }}// Utility class representing group of reactive routesabstractclassExampleRoutes :ReactiveRoutes<ReactiveRoute<CustomScope,Unit>,CustomScope,Unit>()// Endpoint (domain router)classExampleEndpoint(privatevalexampleService:ExampleService) : ExampleRoutes() {// you can use suspend functions in coroutines context// and as long as they're truly reactive, they won't freeze itprivateval nonBlockingAsync= reactiveRoute("/async",GET) { result(nonBlockingDelay("Non-blocking Async")) }overridefunroutes()=setOf(nonBlockingAsync)}funmain() {// prepare dependenciesval exampleService=ExampleService()// create coroutines servlet with single-threaded executorval coroutinesServlet=DefaultContextCoroutinesServlet( executorService=Executors.newSingleThreadExecutor(), contextFactory= {CustomScope(it) }, )// setup Javalin with reactive routingJavalin .create { config-> config.reactiveRouting(coroutinesServlet,ExampleEndpoint(exampleService)) } .events { it.serverStopping { coroutinesServlet.prepareShutdown() } it.serverStopped { coroutinesServlet.completeShutdown() } } .start("127.0.0.1",8080)}
Full example:ReactiveRoutingExample.kt
The core module contains shared components for the other modules.The most important functionality is theRouteComparatorwhich is used to sort given set of routes in the correct order by associated route path.
- Reposilite - real world app using Javalin with property-based DSL routing
About
Set of alternative routing implementations for Javalin 5.x & 6.x
Topics
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.