Before telling me that I am crazy, think about it again.
There are plenty of ways to use server-side rendering today, and the practice of sending displayable data through the network has proven to be efficient :
– rendering data logged in the same tool as for backend services
– less client business logic, less coupling between frontends
– multi-channel broadcast on your clients devices (mobile, desktop, television)
Besides, some frameworks are written on top of virtual machines to do the job. Which one specifically ? Node.js
Java has a poor support today, and many companies are considering rewritting their stack just for the dream of going multi-channel.
Some others cannot, … well it is quite expensive.
While the idea of going rogue and starting to rewrite the app seems like the only way to go, there is an intermediairy solution for companies that mainly has code written in Java :
Make your Java api return react elements.
Howto ?
React components are just initiated (or can always be summarized) by a composition of two elements :
– a type
– properties
If there are some advanced features (forward references, lazy loading, derived states, react portals), these can always be wrapped in simple components in the javascript / typescript client.
Example of json that you can write in your http response :
{"type":"table","props":{"id":"test","className":"my-table","children":{"type":"tablerow","props":{"vAlign":"center","children":{"type":"tablecell","props":{"children":{"type":"MyText","props":{"big":true,"bold":true,"danger":false,"children":"Hello"}}}}}}}}
Then this is how an user interface can be sent through a rest endpoint :
@Component class RenderText { fun page() = tag { table }.new { id = "test" className = "my-table" }.setChildren { tag { tr }.new { vAlign = "center" }.setChildren { new { td }.setChildren { MyLib.MyText::class.new { big = true bold = true danger = false children = "Hello" } } } }.toString() }
Of course, just like in javascript, this is programmatic, you can embed whatever logic you want.
To be able to build this above, you need to declare your react components interface in the JVM.
package com.mycompany.service.react object MyLib { interface MyTextProps: React.Props<MyTextProps> { var bold: Boolean? var danger : Boolean? var big : Boolean? } interface MyText : React.FC<MyTextProps> }
You may now ask how can we have the react typings without leaving the JVM. In my case I have used (https://github.com/Kotlin/dukat) to convert the react typescript types back into Kotlin (and then back to the JVM)
You can just copy / paste react 16.13.1 types in kotlin : https://gist.github.com/libetl/7f4784eeaa5320b14b33567c0544c52a#file-react-kt
Now that you have your components and the react library, there is one missing gap : how do I instantiate interfaces ?
I have added just a small set of helpers method to achieve this goal, by creating proxy instances.
These proxy instances are objects which can extend a complex interface without necessarily implementing all the behaviors.
That lets you bind data to your components without needing you to plug the events and interactions… since these will be lost during the serialization.
Just copy paste those “medium sized” builder methods : https://gist.github.com/libetl/7f4784eeaa5320b14b33567c0544c52a#file-helpers-kt.
Your react code in java should now compile… Enjoy some cheap server-side rendering in the JVM now.
That is it for the backend part. Your API is now able to render a react as json.
Next, you have to convert that json into react elements.
I have a javascript ES5 file to do that : https://gist.github.com/libetl/7f4784eeaa5320b14b33567c0544c52a#file-expand-react-json-js.
Finally, you need the html page to fetch the API that you have created and to transform the result into components :
That is it,
And let’s be patient. Someday we are going to hear about Jetpack Compose for the web.
Until next time, goodbye.