Universal-json tool from OCaml natively and melange

Hey everyone! This is my first post here. I’m a newcomer from Reasonml and having a lot of fun with OCaml. I’m trying everything to learn it better. One of these experiments is understanding and working with universal code for the OCaml server and client.

@davesnx and I have discussed a universal JSON for native and melange apps.
I created a PR on server-reason-react to provide melange-json support to native code based on yojson. ###### (Sorry, David, for spamming you into every post about) :melting_face:

We did face some issues, such as the JSON type, which on melange-json uses Js.Json.t, and the native code cannot use Js.Json.t easily without tidying it to the Yojson contract. Another issue was that server-reason-react is too related to melange inner types, and it could set the contract too much focused on melange-json and melange. You can find more details at this link: Set type t for a better approach on universal code · Issue #9 · melange-community/melange-json · GitHub.

So, I started a new universal-json project, which Yojson and melange-json strongly inspire. You can find more details at this link: GitHub - pedrobslisboa/universal-json: Universal JSON handlers for natively and js apps.

The idea is to have an all-in-one code for handling JSON without externals and with solid types. Maybe the idea is just overengineering, so I would love your feedback and comments.

It was fun to create it :tada:

1 Like

The server-reason-react PR link The link is here: Feat/melange json by pedrobslisboa · Pull Request #133 · ml-in-barcelona/server-reason-react · GitHub.

As a new user, I couldn’t post with 3 links.

1 Like

I didn’t have a deep look at universal-json, but I just want to mention Json_repr in json-data-encoding: json-data-encoding 1.0.1 (latest) · OCaml Package. It also offers (extensible) abstraction over different JSON representations. And there seems to be some kind of browser version as well: https://ocaml.org/p/json-data-encoding-browser/1.0.1/doc/Json_repr_browser/Repr/index.html.

The (json-)data-encoding libraries offer a lot more than that. An universal representation isn’t their primary goal.

1 Like

Yeah, and that was one of the problems we ran into with universal code, needing the same contract for both JS and Native OCaml to work on apps with SSR for example. That’s where the idea of Universal JSON came from. There aren’t many libraries that handle this, especially since server-reason-react is still in its alpha-stage.

Could you say more details what is wrong with Yojson? I didn’t get it from the 1 paragraph

I’m sorry; I forgot to contextualize it.

TLDR: Yojson doesn’t support Melange; the alternative on the client is melange-json, which doesn’t support Ocaml native. We want a universal code that could run on both sides with the same file and syntax. To achieve it, I created (primarily for fun) this universal code inspired by Yojson, which has a similar contract to melange-json but can run on native ocaml and Melange. So there is nothing wrong with Yojson, it just can’t (for now) fulfill the universal needs.

The SSR problem with reason-react

We have a while the Reasonml and Reason-React allow us to build React applications with Reasonml, which compiles to JS.

One of the problems front-end developers face with React is that everything is rendered on the browser. This has costs for UX because the page can be slow to appear, and for SEO because robots can’t see the markup with the semantic details since they are going to be built after running the JS and then running the render function from React, adding all the applications to the DOM.

It would be nice to see the application (All the structure and styles) before the JS and react start to work. React provides a way to handle it with the renderToString which runs your application into an HTML-structured string. That string is provided to the browser, and React hydrates it instead of rendering it; this can be a lot faster than Client-side rendering. So, the same app entry point can run on both server and client, but it is only compatible with the node. Then, to use reason-react for render on SSR, we need to have our reasonml code compiled to a JS on the server.

Here is where server-reason-react enters. It is a library that allows us to have a Server-side rendering react app with a native OCaml/reason server. It works because it creates an OCaml native way to handling with reason-react code.
It can be 10x faster than node on rendering, reference: Server-side rendering React in OCaml | sancho.dev.

So it works like this:

Step II and Step V should use the same code. Components/Utils/Hooks/Services should work both for the browser engine (JS) and the OCaml/Reason server as @davesnx demonstrated here: Server-side rendering React in OCaml | sancho.dev.

However, many libraries have been developed for the client (Melange) that can be run on a native OCaml server. We need universal code that can run on both native and compiled JS, sometimes adapting it, such as server-reason-react.js which universalize part of the Melange Js module.

So, yojson isn’t available to work for Melange, and melange-json isn’t meant to work on native serve either. The alternatives was to universalize the melange-json to ocaml, universalize yojson to Melange, or create an already universalized code. As I’m learning more of ocaml I decide to create it as a community helper to handle JSON on reason universal applications, and have some fun.

I don’t know if I was too deep, but it is here for anyone how wants to know. :3

Does it make sense @Kakadu ?

1 Like

As a newcomer I couldn’t post 2 images and more than 2 links, so here it is some additional info:

Missing image

Client-side render works like this:

You can see the difference between Server-side rendering and Client-side rendering.

Missing links

React renderToString: https://react.dev/reference/react-dom/server/renderToString
Full stack reasonml App running with server-reason-react: GitHub - pedrobslisboa/full-stack-reasonml-advanced-sample: A Full Stack native reason-react application with getInitialProps