NodeJS SSR in VueJS CLI
Tags: vue, node, ssr
Published: 2022-01-29 10:01:41

Have you ever thought about building a front-end app with the amazing Vue CLI? Well, if you're here I think you had.
Perfect, have you thought about improving the performances of your Vue app with Server Side Rendering (SSR) in NodeJS? If yes, then keep reading, my friend!

Wow, 2 shots on 2!

## Let's get started
First of all, create the Vue project with the Vue cli, so run `vue create my-first-ssr-project`, then move into the server dir and run `npm init` to initialize your dir as a Node project.

Cool, we've done nothing than a basic setup now.

Now that we're ready just logically think of it in this way:

1. Building our Vue project as we usually do.
2. Serving it from the NodeJS server.

Okay cool, but how?

### The VueJS client
I found this solution: just rely on some good old rendering technique usable in Node (I found Eta, it's fast, easy to use and oh, the most important: **it supports custom delimiters!**)
To actually implement this, I decided for this approach, and guess? It worked perfectly!

Just go to your `vue.config.js` and write down this: 

```javascript
module.exports = defineConfig({
    // Other vue config up here and down there
    chainWebpack: (config) => {
        if (process.env.NODE_ENV === 'production') { // Only use this in production mode, I'm not sure this is actually needed tho
            config
                .plugin('html') // Tell webpack you want to configure the HtmlWebpackPlugin
                .tap(args => { // Actually configure the plugin
                    args[0].filename = 'index.eta'; // The name of the output you want. Note the extension
                    return args;
                });
        }
    },
})
```

Okay, let's explain it a bit. 
You probably know Vue CLI uses Webpack under the hood to build your app. Well, Webpack to actually build it uses the HtmlWebpackPlugin which (optionally) takes a file as input, called template, and outputs a file, called filename. Perfect. 

Now keep in mind: the template is the `index.html` file Vue CLI generated for you during the setup, located in the public folder, the filename is the output file that Vue CLI outputs in the dist folder after you run `npm run build`.

### The NodeJS server
Now let's install all the dependencies and configure the server.
First, run `npm install express eta`, then let's create a simple server.js file with the following:
```javascript
const express = require('express');
const eta = require('eta');
const path = require('path');
const app = express();

const STATIC = path.join(__dirname, 'public');

eta.configure({
  tags: ["{{", "}}"] // These are the delimiters, you may use whatever you like, I like these to keep consistency with the Vue app
});

app.use('/', express.static(STATIC)); // Remember to use this to serve also your CSS and JS files, otherwise you'll be able to serve only the index file!

app.get('/', async (req, res) => {
  const HTML = await eta.renderFile(STATIC + '/index.eta', {
    title: "Your dynamic title",
    content: "This content will be dynamic (server rendered)" 
  });
  res.send(HTML);
})

app.listen(3000, () => { console.log("Server is listening on port 3000") })

```

Okay now we have our backend completed, we just need one more thing to do: create our template for Eta in Vue!

Remember when I told you about the custom delimiters? 
Well, they're mandatory because usually rendering engines uses these as delimiters `<% CONTENT %>`, and guess what? So does Webpack to generate the default index.html file during our app build, so to "make everything work" we need to remember this: in our template file (the one in the Vue project, public directory) we will have 2 different syntaxes: the one with `<% %>`, used by Webpack to inject things like the BASE_URL and stuff, and the one with `{{ }}`, used by Eta in the SSR part.

**Plus tip:** Eta dynamic variables are accessible from a "it" magically global, but you can change it (see [Eta configuration](https://eta.js.org/docs/api/configuration)).


Now you can use it however you want! Cool right?
So here we have our index.html which will be parsed by Webpack and will become `index.eta`, ready to be parsed by Eta in our server.

```html
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <h1>{{= it.title }}</h1>
    <p>{{= it.content }}</p>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
```

Noticed the equals (=) after the opening delimiters? That's mandatory for Eta to parse different type of things.

### Conclusions
To test everything, we need to move our /dist directory from the Vue project to a directory called /public inside the server directory, then go! Run `node server.js` from your server root, go to localhost:3000 and you'll see your SSR Vue app!

**Production tip:** you can change the outputDir option in `vue.config.js` to automatically build your Vue project inside the server's /public folder.

This trick could even work in a reversed side (changing the HtmlWebpackPlugin delimiters, if that's even possibile) and then using any rendering engine on the server side, becausa remember: it's just a matter of collision! Server delimiters and client delimiters have to be different to make it all work!

Hope to have helped someone and let me know!