Custom JSON mapper

Context#json used to rely on Jackson to do JSON mapping. As of 1.7.0 it relies on JavalinJsonPlugin. This plugin holds a default implementation for Jackson, but can be configured to use whatever you like.

This is how to use GSON instead of Jackson:

Gson gson = new GsonBuilder().create();

jTwig templates

jTwig is the Java implementation of a very popular PHP template engine. This can now be used via Context#renderJtwig.


  • Javalin’s constructor is now protected instead of private. This means you can subclass Javalin.
  • Javalin now has disableStartupBanner(), which removes the Javalin banner from logs
  • Javalin will now print more helpful error messages if the current port can’t be used.

Context extensions

Context extensions give Java developers a way of extending the Context object.

One of the most popular features of Kotlin is extension functions. When working with an object you don’t own in Java, you often end up making MyUtil.action(object, ...). If you, for example, want to serialize an object and set it as the result on the Context, you might do:

app.get("/", ctx -> MyMapperUtil.serialize(ctx, myMapper, myObject)); // three args, what happens where?

With context extensions you can add custom extensions on the context:

app.get("/", ctx -> ctx.use(MyMapper.class).serialize(object)); // use MyMapper to serialize object

Context extensions have to be added before you can use them, this would typically be done in the first before filter of your app:

app.before(ctx -> ctx.register(MyMapper.class, new MyMapper(ctx, otherDependency));

You could also create a general extension class which holds all your extensions:

VelocityEngine velocityEngine = new VelocityEngine();
Gson gson = new GsonBuilder().create();

app.before(ctx -> ctx.register(Ext.class, new Ext(ctx, gson, velocityEngine)));

Extensions are very powerful, you can use them for any action performed on the Context. Another example is parameter extraction:

public class Ext {
    private Context ctx;
    public Ext(Context ctx) {
        this.ctx = ctx;
    public String formUsername() {
        String username = ctx.formParam("username");
        if (userName == null) {
            throw new InvalidFormException();
        return username;
    public String userLocale() {
        return ctx.queryParam("locale");
} -> {
    String username = ctx.use(Ext.class).formUsername();
    String locale = ctx.use(Ext.class).userLocale();

As you see with the throw, extensions can also complicate your application. Use them with caution.