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:
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
Top comments (10)
How do you serve this frontend with Nest.js?
Why do you want to serve it with nestjs?
Each frontend and backend should be its own process or server.
Serving them from the same process or server would defeat the purpose of splitting them up.
I see that there is Redis.
Might you put a Nginx tutorial?
Nginx is something to setup for production.
So if I will use Nginx this will come later :)
What about SSR?
HI, thank you for articles, easy to read, except this one.
It named: "NestJS - Adding a frontend to the monorepo"
But in fact you created a separate folder to nestjs apps and in your vuejs app there is no api calls to nestjs app.
Maybe I'm missing something, please, correct me if I'm wrong.
Thank you.
Hey,
I'm confused a little bit :D
You can still make api calls to nestjs?
Or is it because of no examples in this post?
Nest and the frontend should talk over http anyway so in production they can live on separate servers.
So yeah I'm confused :D
😄
My idea is that this part can live separately from nest articles, because:
I'd either separate it or update by showing how to include view app in the nest folders stricture and added one simple API call
I hope I clarified welly comment this time 🙂
Okay.
At the end of the day, you need to use what fits you and the project the most.
So yeah you don't need to separate them ore make a mono repo.
You do you and what fits the project the most :)
Nice!
Do you have the full repo on Github or somewhere?