DEV Community

Felipe Guizar Diaz
Felipe Guizar Diaz

Posted on

Universal Rendering in Flask using Vue.js and Ara Framework

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

Nowdays, 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

It's a proxy server part of Ara Framework to implement Universal 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:

  1. A user requests a page to Nova Proxy.

  2. Nova Proxy passes the request to the website server.

  3. The website uses a Nova Directive to render the placeholders where the Nova views should be included.

  4. The website sends back the HTML generated to Nova Proxy.

  5. Nova Proxy include the Nova views on 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 Flask App

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

Install SAO:

npm i -g sao

Create Flask app:

sao marconi1992/create-flask-app ara-flask

Set Up 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
# Initialize the app
app = Flask(__name__, instance_relative_config=True)

# Load the views
from app import views

# Load the config file
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:

flask page

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>\n<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 to run 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 --config ./nova-proxy.json

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 the it.

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

flask-example-vue-render

The rendered view is not interactive yet, if we type something in the input element the heading text is not updated. It's 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:

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

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

interactive-nova-view

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.

Top comments (0)