Kotlin: when the null-safety is not safe with Spring Boot or Micronaut
I love Kotlin. I really do. Since I knew about this language it became my favorite programming language.
I have been working with Kotlin for quite a bit, and learned about functional programming with Kotlin and tons of other benefits of this programming language, specially if you compare it with Java.
But, a few days ago I found something that could lead to some major problems when working with Kotlin and Spring Boot or Micronaut.
When you work with Kotlin one of the biggest benefits is to get rid of the NullPointerException problem. It’s so easy to feel safe with this! But sadly this is not 100% accurate, not at least when you work with Spring Boot or Micronaut, and I’ll tell you why.
I was facing some “random” problem while working in the Chameleon project ,where somehow a list of no nullables strings were stored in my database with a null value. It looked like a random error, and since I had some bugs in my previous code I didn’t pay too much attention to it, but a few days ago it happened again, and I thought “Ok, this is not an old bug, this is new code. Something weird is happening…” so I started to look under the hood to see what was happening and then I saw something I couldn’t believe it could happen.
A List<String> was storing null values in a incoming request!!
How this could be happening? This was supposed to be impossible since Kotlin says it must be not null!
Proof from a PoC I did to show this error to a friend:
I guess, now you trust me so I’ll tell you why this is happening and how you can avoid this problem
Why is this happening?
Let me show you this image
If you see this picture you can see that you can create a List of String with null elements of an object that is suppose to not allow null elements in his list because Foo was created in Kotlin saying the List must contain not null elements, but here in Java we can make this run without any problems at all.
Well, this is the same problem that’s happening above. Since Spring Boot and Micronaut are written in Java, and when you transform your body to the object this is done in Java, it doesn’t care about null-safety, therefore you can have this situation where a list of a allegedly non-null String can be null.
For me this problem was dramatic, because I store that String in my database with a null value, and when I read it again my backend crashed saying that I was trying to set null to a non-null value and this was happening because I was using KMongo which is written in Kotlin and the null-safety is well checked there.
And is not only that the backend crashed where it was not supposed to crash, is that you have some corrupted data in your database that cannot be trusted anymore and you are exposed to people with bad intentions.
How do i fix this?
You can use a Kotlin based framework like Ktor or I started using the excellent GraphQL Kotlin library from Expedia, but since it’s obvious that some of you couldn’t or don’t want to change the frameworks you are using, I’m giving a solution for all the people that still want to use Spring Boot and Micronaut.
The easiest way to fix this is one of these:
- Create an annotation to validate that the Strings inside the list are not null
- Encapsulate the data
I chose to do the second one since it give me more power of validation. So let’s go back to the previous example and encapsulate that data.
(I wasn’t able to make this run in Spring Boot so this example is built with Micronaut which is similar)
I have added the Valid annotations and encapsulate the String in a class called LabelDto and inside that I have added the NotBlank annotation
Now, if you make the same API call you get this
Well, you can also do this without encapsulating the data but you will have errors in your console log and the user won’t receive any descriptive error apart from a 500 status code. You choose, but I prefer descriptive error messages :)
I hope you liked this post and if you are working with Kotlin for server-side development pay attention to this and if this post helps at least one person to don’t have this problem, this will have been worth it :)