Giter Site home page Giter Site logo

javalin / javalin Goto Github PK

View Code? Open in Web Editor NEW
7.2K 113.0 554.0 4.88 MB

A simple and modern Java and Kotlin web framework

Home Page: https://javalin.io

License: Apache License 2.0

Java 10.63% HTML 0.12% JavaScript 0.02% CSS 0.01% Kotlin 88.95% Vue 0.27% FreeMarker 0.01% Twig 0.01% Mustache 0.01% StringTemplate 0.01%
kotlin java rest-api web-framework microservice servlet jetty javalin hacktoberfest

javalin's Introduction

Logo

A simple web framework for Java and Kotlin


Static Badge Stable Version Snapshot Version Discord Link

Javalin is a very lightweight web framework for Kotlin and Java which supports WebSockets, HTTP2 and async requests. Javalin’s main goals are simplicity, a great developer experience, and first class interoperability between Kotlin and Java.

Javalin is more of a library than a framework. Some key points:

  • You don't need to extend anything
  • There are no @Annotations
  • There is no reflection
  • There is no other magic; just code.

General information

Community

We have a very active Discord server where you can get help quickly. We also have a (much less active) Slack if you prefer that.

Contributions are very welcome, you can read more about contributing in our guide: CONTRIBUTING

Please consider ❤️ Sponsoring or starring Javalin if you want to support the project.

Quickstart

Add dependency

Maven

<dependency>
    <groupId>io.javalin</groupId>
    <artifactId>javalin</artifactId>
    <version>6.1.3</version>
</dependency>

Gradle

implementation("io.javalin:javalin:6.1.3")

Start programming (Java)

import io.javalin.Javalin;

public class HelloWorld {
    public static void main(String[] args) {
        var app = Javalin.create(/*config*/)
            .get("/", ctx -> ctx.result("Hello World"))
            .start(7070);
    }
}

Start programming (Kotlin)

import io.javalin.Javalin

fun main() {
    val app = Javalin.create(/*config*/)
        .get("/") { it.result("Hello World") }
        .start(7070)
}

Examples

This section contains a few examples, mostly just extracted from the docs. All examples are in Kotlin, but you can find them in Java in the documentation (it's just syntax changes).

You can find more examples in the javalin-samples repository.

Api structure and server config

import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.*

fun main() {
    val app = Javalin.create { config ->
        config.useVirtualThreads = true
        config.http.asyncTimeout = 10_000L
        config.staticFiles.add("/public")
        config.staticFiles.enableWebjars()
        config.router.apiBuilder {
            path("/users") {
                get(UserController::getAll)
                post(UserController::create)
                path("/{userId}") {
                    get(UserController::getOne)
                    patch(UserController::update)
                    delete(UserController::delete)
                }
                ws("/events", userController::webSocketEvents)
            }
        }
    }.start(7070)
}

WebSockets

app.ws("/websocket/{path}") { ws ->
    ws.onConnect { ctx -> println("Connected") }
    ws.onMessage { ctx ->
        val user = ctx.message<User>() // convert from json string to object
        ctx.send(user) //  convert to json string and send back
    }
    ws.onClose { ctx -> println("Closed") }
    ws.onError { ctx -> println("Errored") }
}

Filters and Mappers

app.before("/some-path/*") { ctx -> ... } // runs before requests to /some-path/*
app.before { ctx -> ... } // runs before all requests
app.after { ctx -> ... } // runs after all requests
app.exception(Exception.class) { e, ctx -> ... } // runs if uncaught Exception
app.error(404) { ctx -> ... } // runs if status is 404 (after all other handlers)

app.wsBefore("/some-path/*") { ws -> ... } // runs before ws events on /some-path/*
app.wsBefore { ws -> ... } // runs before all ws events
app.wsAfter { ws -> ... } // runs after all ws events
app.wsException(Exception.class) { e, ctx -> ... } // runs if uncaught Exception in ws handler

JSON-mapping

var todos = arrayOf(...)
app.get("/todos") { ctx -> // map array of Todos to json-string
    ctx.json(todos)
}
app.put("/todos") { ctx -> // map request-body (json) to array of Todos
    todos = ctx.body<Array<Todo>>()
    ctx.status(204)
}

File uploads

app.post("/upload") { ctx ->
    ctx.uploadedFiles("files").forEach { uploadedFile ->
        FileUtil.streamToFile(uploadedFile.content(), "upload/${uploadedFile.filename()}")
    }
}

Plugins

Javalin has a plugin system that allows you to add functionality to the core library. You can find a list of plugins here.

Installing a plugin is as easy as adding a dependency to your project and registering it with Javalin:

Javalin.create { config ->
    config.registerPlugin(MyPlugin())
}

Some of the most popular plugins are:

OpenAPI Plugin

The Javalin OpenAPI plugin allows you to generate an OpenAPI 3.0 specification for your API at compile time.

Annotate your routes with @OpenApi to generate the specification:

@OpenApi(
    summary = "Get all users",
    operationId = "getAllUsers",
    tags = ["User"],
    responses = [OpenApiResponse("200", [OpenApiContent(Array<User>::class)])],
    path = "/users",
    methods = [HttpMethod.GET]
)
fun getAll(ctx: Context) {
    ctx.json(UserService.getAll())
}

Swagger UI and ReDoc UI implementations for viewing the generated specification in your browser are also available.

For more information, see the Javalin OpenAPI Wiki.

SSL Plugin

The Javalin SSL plugin allows you to easily configure SSL for your Javalin server, supporting a variety of formats such as PEM, PKCS12, DER, P7B, and JKS.

Enabling SSL on the 443 port is as easy as:

val plugin = SSLPlugin { conf ->
    conf.pemFromPath("/path/to/cert.pem", "/path/to/key.pem")
}

Javalin.create { javalinConfig ->
    javalinConfig.plugins.register(plugin)
}.start()

Sponsors

Special thanks

javalin's People

Contributors

28smiles avatar 7agustibm avatar bseib avatar casid avatar chsfleury avatar dependabot[bot] avatar dherges avatar dzikoysk avatar flyrev avatar gmugra avatar hex-agon avatar jorunfa avatar maxemann96 avatar mpe85 avatar mrthreepwood avatar nixxcode avatar oharaandrew314 avatar playacem avatar ralphsteinhagen avatar se7kn8 avatar sealedtx avatar shikasd avatar smithkevin avatar spinscale avatar tareqk avatar tipsy avatar tomhillgreyridge avatar vn7n24fzkq avatar wilerson avatar zugazagoitia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

javalin's Issues

Rewrite methods to include get/set in method names?

Should consider rewriting port(0), enableStaticFiles() etc into setPort(0), setStaticFileLocation("/") etc. This because it would work better with kotlin, and you could do:

val app = Javalin.create().apply {
    port = 0
    staticFileLocation = "/public"
    embeddedServer = ...
}.start()

Groovy sample?

I tried:

@Grapes([
@Grab(group='org.slf4j', module='slf4j-simple', version='1.7.25'),
@Grab('io.javalin:javalin:0.3.7')
])
import io.javalin.Javalin

Javalin app = Javalin.start(7000)
app.get("/") {ctx -> ctx.result("Hello World")}

but it exits immediately... shouldn't it run until ctrl+c?

HTTP Basic Auth Support

Is HTTP Basic Auth supported yet? The Website Documentation does not mention it yet. Such support would be really great especially since Javalin is all about REST APIs which are often protected by using Basic Authentification.

Add tutorial about how to deploy via Docker

Docker containers are perfect for deploying microservices. Instead of deploying to Heroku and then having to pay lots of money when your service becomes popular and you have to scale up and out, or complicated configuration management when manually scaling out your own cloud hosted servers, you can create a simple Dockerfile for your fat jar and a Docker Compose file for running a Docker Swarm cluster.

I didn't test the following, so it's just an example and can be refined in the comments below:

Dockerfile

FROM openjdk:8-jre-alpine

WORKDIR /home
COPY target/my-javalin.jar /my-javalin.jar

EXPOSE 7000

ENTRYPOINT ["java", "-jar", "/my-javalin.jar"]

This can be improved as described here, but is fine for the beginning.

Build Docker image: docker build -t my-name/my-javalin:0.1.0-alpine .

docker-compose.yml

version: "3"

services:

  redis:
    image: redis:3.2-alpine
    ports:
      - "6379"
    volumes:
      - db-data: /data
    networks:
      - libraryapp
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

  my-javalin:
    image: my-name/my-javalin:0.1.0-alpine
    ports:
      - 80:7000
    networks:
      - libraryapp
    depends_on:
      - redis
    deploy:
      mode: replicated
      replicas: 2

networks:
  libraryapp:

volumes:
  db-data:

To run the app on a Docker Swarm cluster: docker stack deploy --compose-file docker-compose.yml my-javalin

When run on a Swarm cluster with one manager and one worker node, this should lead to the redis and one my-javalin container running on the manager node, and one my-javalin container running on the worker node, with automatic load balancing when requests reach either of the two nodes.

Any way to convince Jetty instance to hot reload when running via gradle?

I am using Gradle and the kotlin and application plugins to start my Javalin app. I would like to convince Gradle and Jetty to watch and build my code, and hot-reload the Jetty server.

I was hoping that Gradle's "continuous build" feature could be used to trigger rebuilds, and that putting the Jetty server in debug mode might help, but neither of those seem to work. The continuous build seems to be blocked by Jetty taking over the thread, and only works for other commands that "end", such as my unit tests. Perhaps the debug params will only work with the Gradle jetty plugin?

Anyway, I realize this isn't really an issue with Javalin itself, but I was hoping someone in the community might have some insight on how I could improve my Javalin setup to allow for hot-reloads.

Server stuck on Exception

If an error occurs while executing the application logic, then sometimes the server stucks on it. Helps pressing Ctrl-C. Then the server continues to work and processes even those requests that came to it before

NPE if static-resource folder doesn't exist (or empty?)

[Thread-0] ERROR io.javalin.Javalin - Failed to start Javalin
java.lang.NullPointerException
    at io.javalin.embeddedserver.jetty.JettyResourceHandler.<init>(JettyResourceHandler.kt:29)
    at io.javalin.embeddedserver.jetty.EmbeddedJettyFactory.create(EmbeddedJettyFactory.kt:35)
    at io.javalin.Javalin.lambda$start$1(Javalin.java:74)
    at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-0" java.lang.NullPointerException
    at io.javalin.Javalin.lambda$start$1(Javalin.java:83)
    at java.lang.Thread.run(Thread.java:748)

Describe what differentiates Javalin from other micro web frameworks

Your website currently contains:

Javalin started as a fork of the Spark framework but quickly turned into a ground-up rewrite influenced by express.js.

As a Java developer looking for a simple web framework for a simple RESTful API, not knowing express.js, this doesn't help me a lot. I need to dive into code examples to find out. And what has been rewritten internally?

Add an abstract description or feature list or examples that show the differences.

Is it easier? Faster? Smaller memory-footprint?

Maybe add popular Spring Boot and Dropwizard to the comparison.

Consider turning `Context` functions into properties

This would lead to a much neater API, and there would be a clear difference between functions and properties. It might affect performance though 🤔

Kotlin

Before : headerMap()["Accepts"]
After : headerMap["Accepts"]

Java

Before : ctx.headerMap().get("Accepts")
After : ctx.headerMap.get("Accepts") (if using @JvmStatic )
After : ctx.getHeaderMap().get("Accepts")

Look into ApiBuilder suggestion

@daviddenton makes some suggestions in a gist, which look pretty neat.

Todo:

  • Implement suggestion
  • See how it feels
  • Figure out if/how it will work with Java
// add this method, and verb variants to ApiBuilder...
public static void get(String path, Function<Javalin, Handler> fn) {
    staticInstance().get(prefixPath(path), fn.apply(staticInstance()));
}

// then you can remove the extra methods from ApiBuilder and Javalin which take the overloads

-------------

val myHandler = Handler { it.status(200) }

// this extension function will fix the readability problem IMHO, and add the security behaviour that you want.
fun Handler.securedBy(vararg roles: Role): (Javalin) -> Handler =
    { j -> Handler { ctx -> j.accessManager.manage(this, ctx, roles.toList()) } }

val whatever = path("users") {
    get("/foo", myHandler.securedBy(ApiRole.ANYONE, ApiRole.USER_READ))
}

Request.bodyParam() return null and cause stacktrace

Hi ! I cloned your sample tipsy/javalin-kotlin-example to try your technology which I found very interesting !

Explanations

After the repo was cloned, I imported the project on IntelliJ (ultimate edition). I ran the project from the Main.kt class and I tried some of the API routes.

All the routes using the GET method work totally fine but the routes which need to parse the request body trigger a stacktrace in the console and the creation or update is aborted.

Those routes doesn't work :

  • http://localhost:7000/users/create
  • http://localhost:7000/users/update/:id

After some debugging, I found that the method bodyParam() in the Request class return null.

Here an example of a request which does not work :

  • route: http://localhost:7000/users/create
  • method: POST
  • body: { "name":"Michel", "email":"[email protected]" }

When I do println(req.body()) at the top of the method, I get this :

{
	"name": "Michel",
	"email": "[email protected]"
}

So, the body is well retrieved in the request !

But when I do println("name="+req.bodyParam("name")+", email="+req.bodyParam("email")), I get :

name=null, email=null

Stacktraces

Here the first stacktrace when we use the alias Request.bp(key: String) setted up at the bottom of the Main.kt file :

java.lang.IllegalStateException: this.bodyParam(key) must not be null
	at app.MainKt.bp(Main.kt:59)
	at app.MainKt$main$$inlined$with$lambda$4.handle(Main.kt:28)
	at io.javalin.core.JavalinServlet.service(JavalinServlet.java:62)
	at io.javalin.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:35)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1568)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.Server.handle(Server.java:564)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:317)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
	at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
	at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
	at java.lang.Thread.run(Thread.java:745)

Then, this is the stacktrace when we use the bodyParam() method :

java.lang.IllegalStateException: req.bodyParam("name") must not be null
	at app.MainKt$main$$inlined$with$lambda$4.handle(Main.kt:28)
	at io.javalin.core.JavalinServlet.service(JavalinServlet.java:62)
	at io.javalin.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:35)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1568)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.Server.handle(Server.java:564)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:317)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
	at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
	at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
	at java.lang.Thread.run(Thread.java:745)

Get list of all formParams

In context are many methods to get params map (ctx.paramMap(), ctx.queryParamMap()) ... but I need to get formParamMap() (all fields from x-www-form-urlencoded request) and this method is missing. Can you add this method to context?

Thanks!

Where is path() ?

The documentation refers to the path() grouping function but I seem to not find it available neither in IDE nor in github master branch. Who is wrong ?

Websocket timeout on read

How can we adjust the timeout for websocket?

I'm seeing connections erroring out with Timeout on Read which seems to be due to the timeout settings.

Response.body(InputStream)

Being able to connect an InputStream to the response would be very helpful. I've used this regularly in Spark (just returning an InputStream from the handler). Any chance of adding this?

Path routing not working

Hey,
I have just been trying out Javalin (with Kotlin) and it's amazing how fast one can build APIs with it!
I just wanted to run my app though and ran into following issue: I have routes with a paths but Javalin does not seem to recognize and route them:

This is part of my code

server.routes {
    path("user") {
        get("login", controller::handleLogin)
    }
}

when opening localhost:port/user/login though, I just get a "Not Found" message and the code in the controller is not called.

If I leave the path away everything is fine.

I would be happy about any tips.

Remove path-param from handlers inside path()?

path("users", () -> { 
    get(UserController::getAllUsers); 
    post(UserController::createUser); 
    path(":id", () -> { 
        get(UserController::getUser);
        patch(UserController::updateUser); 
        delete(UserController::deleteUser); 
    } 
}

Add JSON support to req.bodyParam

As reported in #12, req.bodyParam currently only supports key=value syntax.
Would be neat if it could support JSON.
I don't really want to depend on a library to do this. Would it be good enough it req.bodyParam support 1 level json?

Easier access to request-session

fun sessionAttribute(attribute: String, value: Any) = servletRequest.session.setAttribute(attribute, value)

@Suppress("UNCHECKED_CAST")
fun <T> sessionAttribute(attribute: String): T = servletRequest.session.getAttribute(attribute) as T

@Suppress("UNCHECKED_CAST")
fun <T> sessionAttributeMap(): Map<String, T> = servletRequest.session.attributeNames.asSequence().map { it to servletRequest.session.getAttribute(it) as T }.toMap()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.