NestJS - Adding a frontend to the monorepo

lampewebdev

Michael "lampe" Lazarski

Posted on March 22, 2020

NestJS - Adding a frontend to the monorepo

In the last two blog posts, we created a Monorepo and integrated Redis. You can find them here:

In this blog post, we will add Vue as our frontend and make it work within our Monorepo.

Installing the dependencies

Let's first get install our dependencies:

yarn add vue

And now our developer dependencies

yarn add -D babel-loader css-loader file-loader html-webpack-plugin node-sass sass-loader url-loader vue-loader vue-template-compiler webpack webpack-bundle-analyzer webpack-cli webpack-dev-server vue-eslint-parser

As you can see, we need to install way more dependencies for development. Most of them are dependencies to make Webpack build and serve our frontend.
Webpack will handle HTML, vue, css, sass and files.

Creating the frontend

First, we need to create a folder named 'frontend'

mkdir frontend

In that folder, we will have all of our 'frontends'. For this example, we want to create our frontend for our 'blog' backend.

cd frontend
mkdir blog

Now we need to create an index.html file. This will be the entry file to the blog frontend.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title>My Vue app with webpack 4</title>
  </head>

  <body>
    <div id="app"></div>
  </body>
</html>

The most important line here is the div with the id="app". VueJS needs this div as an entry point.

The next file we need is a webpack.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const HtmlPlugin = require('html-webpack-plugin');

const config = {
  context: __dirname,
  entry: './src/index.ts',
  output: {
    path: path.resolve(process.cwd(), 'dist/frontend'),
    filename: '[name].[contenthash].js'
  },
  target: 'web',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.ts$/,
        loader: "ts-loader",
        options: { appendTsSuffixTo: [/\.vue$/] },
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.svg$/,
        use: 'file-loader'
      },
      {
        test: /\.png$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              mimetype: 'image/png'
            }
          }
        ]
      }
    ]
  },
  resolve: {
    extensions: [
      '.js',
      '.vue',
      '.tsx',
      '.ts'
    ]
  },
  plugins: [
    new HtmlPlugin({
      template: 'index.html',
      chunksSortMode: 'dependency'
    }),
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
    }),
    new VueLoaderPlugin(),
  ],
  optimization: {
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  devServer: {
    contentBase: path.join(__dirname, 'public'),
    compress: true,
    port: 9000
  }
};

module.exports = config;

Webpack configs are fun! Let's start from the bottom. The devServer will run on port 9000 and will look for files in the public. For that to work, we need to set the context option to __dirname. __dirname will resolve to the path that the directory is currently in, in our case, the blog frontend folder. entry is the file that bootstraps and we will create it next. In the output we need to specify the path. process.cwd() will resolve to the main project folder, and we are adding dist/frontend. This means you can find there our frontend files. The rest is configuration to get Vue running with typescript, to load CSS, SCSS, SVG and png files.

Typescript also needs a config.

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "strict": true,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "module": "es6",
    "moduleResolution": "node",
    "target": "es5",
    "allowJs": true
  },
  "include": [
    "./blog/src/**/*"
  ]
}

This is a pretty standard ts config. We need to include our blog/src folder. Without this, you will get a typescript error.

Now let us create our src/index.ts file, src/App.vue file and src/vue-shim.d.ts.

index.ts:

import Vue from 'vue';
import App from './App.vue';

new Vue({
  el: '#app',
  render: h => h(App),
});

This is the default VueJS setup.

App.vue

<template>
  <h1>lampeweb dev blog</h1>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  data: function() {
    return {
      name: 'Hello World!',
    };
  },
});
</script>

Thanks to our Webpack config we can already use typescript in our Vue components. This file is a simple Vue component which just will display a header with the text lampeweb dev blog.

vue-shim.d.ts:

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

This will make typescript and your editor happy :). Do you want to know more about how declare module works? Leave a comment!

We need now to define our npm scripts next.

{
  "scripts": {
    "f:blog:dev:watch": "webpack-dev-server -d --mode development --config ./frontend/blog/webpack.config.js",
    "f:blog:build": "webpack -p --mode production  --config ./frontend/blog/webpack.config.js"
  }
}

We can now test if everything worked with:

yarn run f:blog:dev:watch

After Webpack has built our frontend, you should see the following:
Alt Text

I hope you liked that post! If you want a follow-up, please comment, like, and share. So I can know that you are interested in content like that!

👋Say Hello! Instagram | Twitter | LinkedIn | Medium | Twitch | YouTube

💖 💪 🙅 🚩
lampewebdev
Michael "lampe" Lazarski

Posted on March 22, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related