SETUP SIMPLE WEBSITE USING NODE.JS, EXPRESS, NUNJUCKS, GULP & NODEMON
Nội Dung Chính
Hello developers, before we start integration of all our components, let’s have a quick review of the below things.
Node.js
Node.js is a server side javascript-based platform. Like other server side platforms ( eg: Rails, Python, PHP… ), this javascript based platform is built on Google Chrome V8 Javascript Engine.
Node.js uses an event-driven architecture & asynchronous or non-blocking I/O model, which improves efficiency and scalability for real-time applications.
Express
Express is a web application server framework for Node.js. Express provide a robust set of features for building single, multi-page and hybrid web applications.
Express allows us to set up middlewares to respond to client HTTP requests. It also provides URL routing which allows to perform different actions and render dynamic HTML pages using any of the template engines like jade, EJS, nunjucks …
Nunjucks
Nunjucks is a rich & powerful templating engine for javascript. It takes inspiration from jinja2 templating system.
Using nunjucks we can inherit templates and make it possible to use the same or a similar layout for all templates.
Gulp
Gulp automates and enhances your application workflow. It is a task runner which uses node.js. Gulp keeps things simple and makes complex tasks manageable using node modules.
Nodemon
After any changes in your node.js application files, your changes will not reflect immediately. To reflect changes you will have to manually restart node.js server each time in a development environment.
Nodemon helps us to avoid such manual restarts. It monitors all changes in your node.js application and automatically restarts the server for a development environment. Nodemon will watch the files in the directory in which nodemon was started, and if any files change, it will automatically restart your node application.
So we are now clear about all the things. Let’s install the required dependencies using the following commands.
Install Node & npm from nodejs.org. or you can directly install it using brew
1
2
$ brew install node.js
$ node -v
Once node js installed upgrade you npm using
1
2
3
4
5
6
$ sudo npm install npm -g
$ npm -v
$ npm install express --save
$ npm install -g gulp
$ npm install nunjucks
$ npm install -g nodemon
Let’s jump on to an integration of all these components into one to make a solid architecture.
1) Create a blank directory in your workspace
1
$ mkdir myapp && cd myapp
2) Init your app using the following command
1
$ npm init
so your “package.json” file is created using the above command. Open package.json in your favourite IDE and update or add the following node module dependency code.
{
“name”: “myapp”,
“private”: true,
“version”: “1.0.0”,
“description”: “example.com”,
“main”: “app.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1″,
“start”: “echo “Starting server” && NODE_ENV=production gulp”
},
“author”: “[email protected]”,
“license”: “ISC”,
“devDependencies”: {
“body-parser”: “~1.13.2”,
“cookie-parser”: “~1.3.5”,
“debug”: “~2.2.0”,
“del”: “^0.1.2”,
“express”: “^4.13.3”,
“gulp”: “^3.9.0”,
“gulp-changed”: “^1.3.0”,
“gulp-concat”: “^2.6.0”,
“gulp-imagemin”: “^2.4.0”,
“gulp-jshint”: “^2.0.0”,
“gulp-livereload”: “^3.8.1”,
“gulp-minify-css”: “^1.2.3”,
“gulp-minify-html”: “^1.0.5”,
“gulp-nodemon”: “^2.0.6”,
“gulp-notify”: “^2.2.0”,
“gulp-plumber”: “^1.0.1”,
“gulp-rev”: “^6.0.1”,
“gulp-sass”: “^2.1.1”,
“gulp-uglify”: “^1.5.1”,
“gulp-util”: “^3.0.7”,
“jshint”: “^2.8.0”,
“morgan”: “~1.6.1”,
“node-neat”: “^1.7.2”,
“nunjucks”: “^2.2.0”,
“shipit-cli”: “^1.4.1”,
“shipit-deploy”: “^2.1.2”
},
“dependencies”: {
“gulp-rev-collector”: “^1.0.2”
}
}
view rawpackage.json hosted with ❤ by GitHub
Install all the npm dependencies from package.json
1
$ npm install
Now let’s move on to the node server setup and nunjucks configuration with a view. so create app.js file on root level and copy the following js code into your app.js
var express = require(‘express’),
nunjucks = require(‘nunjucks’),
path = require(‘path’),
app = express(),
logger = require(‘morgan’),
cookieParser = require(‘cookie-parser’),
bodyParser = require(‘body-parser’);
app.set(‘assets_path’, (process.env.NODE_ENV === ‘production’) ? ‘dist’ : ‘build’);
app.set(‘views’, path.join(__dirname, app.get(‘assets_path’) + ‘/views’));
app.use(logger(‘dev’));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, app.get(‘assets_path’))));
var routes = require(‘./routes/index’),
api_routes = require(‘./routes/api’);
app.set(‘port’, process.env.PORT || 8000);
// Setup nunjucks templating engine
nunjucks.configure(app.get(‘views’), {
autoescape: true,
noCache: true,
watch: true,
express: app
});
// serve index and view partials
app.use(‘/’, routes);
app.use(‘/api/’, api_routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error(‘Not Found’);
err.status = 404;
next(err);
});
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘404.html’);
});
// Kick start our server
app.listen(app.get(‘port’), function() {
console.log(‘Server started on port’, app.get(‘port’));
});
module.exports = app;
view rawapp.js hosted with ❤ by GitHub
In our app.js file, we configured nunjucks as default template rendering engine
1
2
3
4
5
6
7
var
nunjucks = require(
'nunjucks'
);
nunjucks.configure(app.get(
'views'
), {
autoescape:
true
,
noCache:
true
,
watch:
true
,
express: app
});
so using the above configuration, you can directly use nunjucks syntax in your views. Using this you can also extend or include other views in HTML page.
<html>
<head>
<title>
{% block title %}{{ page.title if page.title}} {% endblock %}
</title>
<meta name=”viewport” content=”width=device-width, initial-scale=1″/>
<link rel=”shortcut icon” type=”image/ico” href=”favicon.ico” />
<link href=”/css/style.css” rel=”stylesheet”>
{% block styles %}{% endblock %}
</head>
<body class=”main-wrap”>
{% include “../includes/header.html” %}
<div class=”content-wrap”>
{% block content %}
{% endblock %}
</div>
{% include “../includes/footer.html” %}
<script src=”/js/vendor.js”></script>
<script src=”/js/main.js”></script>
{% block extra_js %}{% endblock %}
</body>
</html>
view rawdefault.html hosted with ❤ by GitHub
{% extends “./layouts/default.html” %}
{% block content %}
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
{% endblock %}
view rawindex.html hosted with ❤ by GitHub
in the above code “index.html” we are extending default the layout (“default.html”).
Let’s render the above index.html in the browser using the following node.js code
var express = require(‘express’),
router = express.Router();
router.get(‘/’, function(req, res) {
res.render(‘index.html’, {
page: {
name: ‘Home’ // config used in view. nunjuck compile this config
}
});
});
module.exports = router;
view rawindex.js hosted with ❤ by GitHub
Now let’s move on to our last configuration of gulp and nodemon. Create gulpfile.js at root level and copy following js code into your gulpfile.js
‘use strict’;
// Dependencies
var gulp = require(‘gulp’),
nodemon = require(‘gulp-nodemon’),
notify = require(‘gulp-notify’),
livereload = require(‘gulp-livereload’),
changed = require(‘gulp-changed’),
del = require(‘del’),
gutil = require(‘gulp-util’),
concat = require(‘gulp-concat’),
plumber = require(‘gulp-plumber’),
imagemin = require(‘gulp-imagemin’),
minifyCSS = require(‘gulp-minify-css’),
minifyHtml = require(‘gulp-minify-html’),
rev = require(‘gulp-rev’),
jshint = require(‘gulp-jshint’),
imagemin = require(‘gulp-imagemin’),
revCollector = require(‘gulp-rev-collector’),
uglify = require(‘gulp-uglify’),
sass = require(‘gulp-sass’);
var paths = {
fontsSrc: ‘src/fonts/’,
htmlSrc: ‘src/views/’,
sassSrc: ‘src/sass/’,
jsSrc: ‘src/js/’,
imgSrc: ‘src/images/’,
buildDir: ‘build/’,
revDir: ‘build/rev/’,
distDir: ‘dist/’
};
var onError = function (err) {
gutil.beep();
gutil.log(gutil.colors.green(err));
},
nodemonServerInit = function(){
livereload.listen();
nodemon({
script: ‘app.js’,
ext: ‘js’
}).on(‘restart’, function(){
gulp.src(‘app.js’)
.pipe(livereload())
.pipe(notify(‘Reloading page, please wait…’));
})
};
if(process.env.NODE_ENV === ‘prod’){
gulp.task(‘default’, [‘dist’]);
}else {
gulp.task(‘default’, [‘build’, ‘watch’]);
}
gulp.task(‘clean’, function(cb) {
del([paths.buildDir, paths.distDir], cb);
});
gulp.task(‘build’, [‘build-html’, ‘build-css’, ‘build-js’, ‘build-images’, ‘build-favicon’, ‘build-fonts’], function (cb) {
nodemonServerInit();
});
gulp.task(‘dist’, [‘dist-html’, ‘dist-js’, ‘dist-css’, ‘dist-images’, ‘dist-favicon’, ‘dist-fonts’], function (cb) {
nodemonServerInit();
});
/*
HTML Tasks
*/
gulp.task(‘build-html’, function() {
return gulp.src(paths.htmlSrc + ‘**/*.html’)
.pipe(gulp.dest(paths.buildDir + ‘views/’))
.pipe(livereload());
});
gulp.task(‘dist-html’, [‘build-html’, ‘dist-js’, ‘dist-css’, ‘dist-images’], function() {
return gulp.src([
paths.revDir + “**/*.json”,
paths.buildDir + ‘views/’ + “**/*.html”
])
.pipe(revCollector())
.pipe(minifyHtml({
conditionals: true,
quotes: true
}))
.pipe(gulp.dest(paths.distDir + ‘views’));
});
/*
CSS tasks
*/
gulp.task(‘build-css’, [‘sass’]);
gulp.task(‘sass’, function () {
return gulp.src(paths.sassSrc + ‘**/*.scss’)
.pipe(sass({
includePaths: require(‘node-neat’).includePaths,
style: ‘nested’,
onError: function(){
console.log(“Error in scss”);
}
}))
.pipe(plumber({ errorHandler: onError }))
.pipe(gulp.dest(paths.buildDir + ‘css/’))
.pipe(livereload());
});
gulp.task(‘dist-css’, [‘build-css’, ‘dist-images’], function() {
return gulp.src([
paths.buildDir + ‘css/*’,
paths.revDir + “images/*.json”
])
.pipe(revCollector())
.pipe(minifyCSS())
.pipe(rev())
.pipe(gulp.dest(paths.distDir + ‘css’))
.pipe(rev.manifest())
.pipe(gulp.dest(paths.revDir + ‘css’));
});
gulp.task(‘build-fonts’, [], function() {
return gulp.src(paths.fontsSrc + ‘**/*.*’)
.pipe(gulp.dest(paths.buildDir + ‘fonts/’))
.pipe(livereload());
});
gulp.task(‘dist-fonts’, [‘build-fonts’], function() {
return gulp.src(‘build/fonts/*’)
.pipe(gulp.dest(paths.distDir + “/fonts/”));
});
/*
JS Tasks
*/
gulp.task(‘build-js’, [‘js’, ‘js-plugins’]);
gulp.task(‘js’, function() {
return gulp.src(paths.jsSrc + ‘*.js’)
.pipe(plumber({ errorHandler: onError }))
.pipe(changed(paths.buildDir + ‘js’))
.pipe(jshint())
.pipe(jshint.reporter(‘default’))
.pipe(gulp.dest(paths.buildDir + ‘js’))
.pipe(livereload());
});
gulp.task(‘js-plugins’, [], function() {
return gulp.src([
‘src/lib/*.js’
])
.pipe(concat(‘vendor.js’))
.pipe(gulp.dest(paths.buildDir + ‘js/’))
.pipe(livereload());
});
gulp.task(‘dist-js’, [‘build-js’], function() {
return gulp.src(paths.buildDir + ‘js/*’)
.pipe(uglify())
.pipe(rev())
.pipe(gulp.dest(paths.distDir + ‘js’))
.pipe(rev.manifest())
.pipe(gulp.dest(paths.revDir + ‘js’));
});
/*
Image Tasks
*/
gulp.task(‘build-images’, function() {
return gulp.src(paths.imgSrc + ‘**/*.+(png|jpeg|jpg|gif|svg|eps)’)
.pipe(changed(paths.buildDir + ‘images’))
.pipe(gulp.dest(paths.buildDir + ‘images’))
.pipe(livereload());
});
gulp.task(‘dist-images’, [‘build-images’], function() {
return gulp.src(paths.buildDir + ‘images/**/*’)
.pipe(imagemin({
progressive: true
}))
.pipe(rev())
.pipe(gulp.dest(paths.distDir + ‘images’))
.pipe(rev.manifest())
.pipe(gulp.dest(paths.revDir + ‘images’));
});
gulp.task(‘build-favicon’, function() {
return gulp.src(‘src/favicon.ico’)
.pipe(changed(paths.buildDir))
.pipe(gulp.dest(paths.buildDir))
.pipe(livereload());
});
gulp.task(‘dist-favicon’, [‘build-favicon’], function() {
return gulp.src(paths.buildDir + ‘favicon.ico’)
.pipe(changed(paths.distDir))
.pipe(gulp.dest(paths.distDir));
});
gulp.task(‘watch’, function () {
gulp.watch([‘src/views/**/*.html’], [‘build-html’]);
gulp.watch(‘src/sass/**’, [‘sass’]);
gulp.watch(paths.jsSrc + ‘**/*.js’, [‘js’]);
gulp.watch(‘src/lib/**’ + ‘**/*.js’, [‘js-plugins’]);
gulp.watch(paths.imgSrc + ‘**/*.+(png|jpeg|jpg|gif|svg)’, [‘build-images’]);
});
view rawgulpfile.js hosted with ❤ by GitHub
The above gulp file added with different tasks for a different purpose. On server start, it will automatically run all tasks and create compressed, minified dist folder and serve all static assets from dist folder. gulpfile run the following tasks on server start
1) compile scss
2) compress & minimize js and libs
3) minify css
4) minify html
5) minify images and add random version to image
so in above gulpfile, we configured nodemon using below function and called this function into our default gulp task. This will automatically restart your node js server on every file change.
1
2
3
4
5
6
7
8
9
10
11
nodemonServerInit =
function
(){
livereload.listen();
nodemon({
script:
'app.js'
,
ext:
'js'
}).on(
'restart'
,
function
(){
gulp.src(
'app.js'
)
.pipe(livereload())
.pipe(notify(
'Reloading page, please wait...'
));
})
}
Once all node modules are installed and gulpfile.js changes are done, your directory structure should look like the image below
So using all these commands your project setup is done. Run the below command to start your server on localhost
1
$ gulp
this command will start your node.js server and serve uncompressed assets from the build directory.
Run below command to run your server on production
1
$ npm start
this command will serve compressed assets from the dist directory.
You could also download sample demo from github account.
Happy coding!
For any queries or help, please drop me an email on [email protected]