Universal Rendering consists in server-side rendered pages and making them interactive on the browser, using the same view components built with React, Vue.js, Angular, etc.

Nowadays, JavaScript frameworks such as Next.js, Nuxt.js and NgUniversal make this duty easier. However, how can we achieve it on non JavaScript frameworks like Flask?

Nova Proxy

Nova proxy is a service to implement Unversal Rendering with any view library (React, Vue.js, etc) on any web platform (Laravel, Flask, etc). For example, in this demo we'll render a view using Vue.js into a web application built with Flask.

How it works:

A user requests a page to the Nova Proxy. The Nova Proxy passes the request to the website server. The website uses a the Nova Directive to render the placeholders where the Nova views should be included. The website sends back the HTML generated to the Nova Proxy. The Nova Proxy includes the Nova views in the placeholders and sends back the HTML to the browser.

Finally, on the browser, JavaScript is used to progressively enhance the application and make it interactive. Read more here about the Nova Architecture.

Set Up The Flask App

We'll use SAO to generate the base Flask application.

Install SAO:

npm i -g sao

Create the Flask app:

sao marconi1992/create-flask-app ara-flask

Set Up The Nova Directive

Notice the requirements.txt file contains a package named hypernova_jinja2_directive. It's used to render the Nova directives using the template engine for Flask (Jinja2).

Set up the nova helper for jinja into __init__.py :

from flask import Flask from hypernova_jinja2_directive import nova app = Flask(__name__, instance_relative_config= True ) from app import views app.config.from_object( 'config' ) app.jinja_env.globals.update(nova=nova)

Run Flask App

The application generated contains a docker-compose.yml file to run the Flask app.

Run it:

docker-compose up -d

Test the Flask application on http://localhost:8000/.

Browser:

Set up the Nova service

We'll create a Nova service using Vue.js.

Install Ara CLI:

npm i -g ara-cli

Create Nova service:

ara new:nova -t vue nova

Go to the Nova service folder:

cd nova

Run Nova service:

npm run dev

The Nova service runs on http://localhost:3000.

Test the Nova Service.

Once the Nova service is running you can make a POST request to http://localhost:3000/batch using a payload like:

{ "uuid" : { "name" : "Example" , "data" : { "title" : "Ara Framework" } } }

The results property in the response contains the html of the view rendered by the Nova service.

Example:

{ "success" : true , "error" : null , "results" : { "uuid" : { "name" : "Example" , "html" : "<div data-hypernova-key=\"Example\" data-hypernova-id=\"4d9e81bd-6413-4661-ab56-ed5bb4f59cae\"><h1 data-server-rendered=\"true\">Ara Framework</h1></div>

<script type=\"application/json\" data-hypernova-key=\"Example\" data-hypernova-id=\"4d9e81bd-6413-4661-ab56-ed5bb4f59cae\"><!--{\"title\":\"Ara Framework\"}--></script>" , "meta" : {}, "duration" : 1.210146 , "statusCode" : 200 , "success" : true , "error" : null } } }

Update the Example view

The Example view is rendering only a simple heading. We can make it interactive adding an input element that changes the heading text.

Replace the nova/src/components/Example.vue with the following code:

< template > < div > < h2 class = "title" > {{title}} </ h2 > < input type = "text" v-model = "title" > </ div > </ template > < script > export default { props : [ 'title' ] } </ script >

Set up Nova Proxy

Create a configuration file for Nova Proxy in the root folder:

touch nova-proxy .json

Add the following configuration in nova-proxy.json file to proxy the incoming requests to the Flask web server.

{ "locations" : [ { "path" : "/" , "host" : "http://localhost:8000" , "modifyResponse" : true } ] }

Run Nova Proxy

Before running the command we need to set the HYPERNOVA_BATCH variable using the Nova service endpoint.

export HYPERNOVA_BATCH=http://localhost:3000/batch

You need to run the following command where the noxa-proxy.json file was created or pass the --config parameter with the configuration file path.

ara run :proxy

The command runs Nova Proxy on http://localhost:8080.

Render Nova views (Vue.js)

Use the Nova directive in a Jinja2 template page:

app/templates/index.html

{% extends "base.html" %} {% block body %} < div class = "container" > < h1 class = "title" > Hello Flask </ h1 > {{ nova('Example', {'title':'Ara Framework'}) }} < div > {% endblock %}

The nova helper requires the name of the view, and the data necessary to render it.

Open the page on http://localhost:8080 and see how the Nova view is rendered.

The rendered view is not interactive yet, if we type something in the input element the heading text is not updated. This is happening because we're not loading the client script.

Hydrate Nova views on the browser

Hydration is the process of mounting a view component on the browser using the state used when it was rendered on the server.

In order to hydrate the views we need to load the client.js script on the browser.

Update the base.html file in the Flask app:

< html lang = "en" > < head > .... </ head > < body > .... {% block body %} {% endblock %} < script src = "http://localhost:3000/public/client.js" > </ script > </ body > </ html >

Finally, the Nova view will be rendered on the server is interactive and dynamic on the browser.

Conclusion

Nova Proxy enables us to use modern view libraries on any web framework. So if you previously developed a web application using non-Javascript frameworks (Laravel, Flask, Ruby on Rails, etc) then Nova Proxy can help you to gradually migrate its views to a JavaScript view library (React, Vue.js) in a short period of time.