-
Create simple web site with Node.js
Using Node.js URL Module
An important part of a web page is the URL address.
The
URL module splits up a web address into readable parts.
This module can be used to create a Node.js script that displays html content according to URL address.
To include the URL module, use the
require() method:
const url = require('url');
Now, lets parse an URL address, using the
url.parse() method. It will return a URL object with each part of the address as properties:
var url = require('url');
var adr = 'http://localhost:8080/index?id=890&ctg=nodejs';
var p = url.parse(adr, true);
console.log(p.host); //returns localhost:8080
console.log(p.pathname); //returns /index
console.log(p.search); //returns ?id=890&ctg=nodejs
var q = p.query; //contains an object: {id:890, ctg:'nodejs'}
console.log(q.id); //returns 890
Split URL.search string
You can use the
query property of the url.parse() method to split the search query string into readable parts. So, to can display different content according to data from URL query.
Example, split the query string into readable parts, and display the resulted object into a JSON string in page.
//include http and url modules
const http = require('http');
const url = require('url');
//create the server
const server = http.createServer((req, res)=> {
res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'}); //adds header to display html content with utf-8
//parse the url, get object with {name:value} from url query
var sq = url.parse(req.url, true).query;
var src_data ='<h4>'+JSON.stringify(sq)+'</h4>';
src_data +='id = '+sq.id+'<br>nm = '+sq.nm;
res.write(src_data, 'utf-8');
res.end();
});
server.listen(8080, ()=> {
console.log('Server running at //localhost:8080/');
});
Save the code above in a file called "demo_url_query.js" and initiate the file with node in the command line interface.
Access in your browser the address:
//localhost:8080/?id=1&nm=admin
Will produce this result:
{"id":"1","nm":"admin"}
id = 1
nm = admin
Create simple web site
Now we know how to parse the URL address, and to make Node.js behave as a server. In the previous tutorials we have learned how to use the "fs" module to read a file content.
Let us combine what we have learned
to create a simple Node js website with categories and pages that serve the page requested by the client.
• For this project you can create the files with the code presented in this page, or ..:
- Click this link to download the whole script:
Get Node.js Simple WebSite.
• To see a Demo, click:
Demo Node.js Simple WebSite.
1. Create the html template file
First, create a folder called "
site1", in the same directory as your node.js.
In the "site1/" folder create a file called "
tpl_index.htm" with a template html document, and a responsive design; which we'll use to serve the html page content.
-
Here is the code for "tpl_index.htm" file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{${title}}</title>
<meta name="description" content="{${description}}"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body, html {
position:relative;
background:#f2f3fe;
margin:0;
padding:0;
text-align:center;
font-size:1em;
font-family:"Calibri",sans-serif;
}
header {
background:#fafaff;
border:1px solid #888;
}
h1 {
margin:1px auto 8px auto;
}
#menu_top{
position:relative;
margin:3px auto;
padding:0;
font-size:16px;
font-weight:700;
}
#menu_top a{
margin:1px 2px;
background-color:#8f9fde;
padding:1px 4px;
text-decoration:none;
color:#fff;
box-shadow:.15em .13em .25em #6789da inset;
-webkit-box-shadow:.15em .13em .25em #6789da inset;
border-radius:.5em;
background-image:-ms-linear-gradient(top, #0818be, #b0c0fb);
background-image:-moz-linear-gradient(top, #0818be, #b0c0fb 95%);
background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #0818be), color-stop(0.9, #b0c0fb));
background-image:-o-linear-gradient(top, #0818be, #b0c0fb);
background:linear-gradient(top, #0818be, #b0c0fb);
}
#menu_top a:hover{
background:#fbfb01;
text-decoration:underline;
color:#0408fe;
box-shadow:.15em .13em .25em #a0a0da;
-webkit-box-shadow:.15em .13em .25em #a0a0da;
}
/* Side Menu */
#menu_side {
position:absolute;
top:100px;
left:1px;
width:200px;
padding:2px 1px 8px 0;
text-align:center;
}
#menu_side h4 {
margin:-.7em .8em .1em .7em;
background-color:#eaebfe;
padding:1px;
box-shadow:.15em .13em .25em #6789da inset;
border-radius:.5em;
background:linear-gradient(top, #dddefe, #eeeffe);
text-align:center;
}
#menu_side ul {
position:relative;
margin:0;
padding:0;
background:#f9f9eb;
border-radius:.6em;
list-style-type:none;
font-size:17px;
text-align:left;
}
#menu_side a {
display:block;
padding:2px 0 1px 4px;
color:#0000c0;
}
#menu_side > ul > li:hover, #menu_side > ul > li a:hover {
background:#3434fe;
color:#ffffff;
text-decoration:none;
}
#content {
background:#fefefe;
position:relative;
margin:25px 2px 5px 215px;
}
/* mobile dev. */
@media screen and (max-width: 650px){
@keyframes slide_top {
0% { margin-top:100px; }
100% { margin-top:0; }
}
header {
margin:1px 1px 3px 28px;
}
#menu_top {
font-size:14px;
line-height:150%;
margin:0 25px 1px 23px;
}
#menu_top a {
margin:2px 1px 2px 2px;
padding:0 2px;
font-weight:700;
}
#menu_top a:hover {
background:yellow;
color:#0408fe;
}
/* Side Menu */
#menu_side {
left:0;
top:35px;
background:#bedacf;
padding:3px;
border-radius:5px;
z-index:8888;
width:auto;
}
#menu_side h4 {
background-color:transparent;
box-shadow:none;
background:none;
font-size:18px;
line-height:85%;
width:20px;
margin:0;
padding:0;
}
#menu_side ul {
background:none;
display:none;
}
#menu_side ul li {
margin:2px auto;
background:#e0e0fb;
padding-left:1px;
}
#menu_side:hover h4 { display:none;}
#menu_side:hover ul {
display:block;
animation-name: slide_top;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-fill-mode: forwards;
}
#content {
margin:15px 1px 3px 28px;
}
}
</style>
</head>
<body>
<header>
<h1>{${title}}</h1>
<nav id="menu_top">{${menu_header}}</nav>
</header>
<section id="content">{${content}}</section>
<nav id="menu_side">
<h4>M E N U</h4>
{${menu_side}}
</nav>
</body>
</html>
2. The folder for files with pages content
In "site1/" directory create another folder called "
content". In this folder we'll store ".htm" files with the content for each page of the site.
Also, in the "content/" folder we create a ".json" file, called "
pages.json" that contains a JSON object with data for categories and their pages.
The first category "/" is for index /home page and other pages in this category.
-
Here is the code for "pages.json" file, with 3 main categories and 5 pages:
{
"/":{
"title":"Title home page",
"description":"Description home page",
"link_txt":"Home",
"pages":{
"contact":{
"title":"Contact page",
"description":"Description for contact page",
"link_txt":"Contact"
}
}
},
"ctg-1":{
"title":"Title category one",
"description":"Description category one",
"link_txt":"Ctg-One",
"pages":{
"page-1":{
"title":"Title page one",
"description":"Description page one",
"link_txt":"Page One"
},
"page-2":{
"title":"Title page two",
"description":"Description page two",
"link_txt":"Page Two"
}
}
},
"ctg-2":{
"title":"Title category two",
"description":"Description category two",
"link_txt":"Ctg-Two",
"pages":{
"pg-one":{
"title":"Title page one in category 2",
"description":"Description page one in category 2",
"link_txt":"Page 1"
},
"pg-two":{
"title":"Title page two in category 2",
"description":"Description page two in category 2",
"link_txt":"Page 2"
}
}
}
}
- This object contains the Title, Description, and text for link ('link_txt') for each category and pages. These data will be added in template when a page is accessed in browser.
The main content for each page will be stored in separate files (in "content/" folder), having the same name as the name added in "pages.json", and ".htm" extension.
- For example: for 'index' page we create a file called "index.htm" with the html content we want to display in page-body. For the page-body content of the "ctg-1" category we create a file named "ctg-1.htm". And so on for the body content of each category and page, like in this image:
3. The JS script for page content
Now, we create a module called "
setpage.js" in the "site1/" folder, with a JavaScript class that will get and set all the html content for the requested page; with data added in the "content/" folder.
This module will be included with require() in the main.js file.
-
Here is the code for "setpage.js" file:
//set the page content according to data from url
class setpage {
//set propertie
constructor(){
this.pages ={} //for categories and pages data stored in pages.json
this.tpl ='' //store string with html template from tpl_index.htm
//values for template
this.tpl_val ={
title:'',
description:'',
menu_header:'',
menu_side:'',
content:''
}
this.ctg ='/'; //current category
this.pg =''; //current page
this.err =[]; //store errors
//gets pages data from pages.json
fs.readFile(path.resolve(__dirname, 'content/pages.json'), 'utf8', (err, data)=>{
if(err) this.err.push(err);
else {
this.pages = JSON.parse(data);
if(this.pages) this.menuHeader(this.pages);
}
});
//gets template from tpl_index.htm
fs.readFile(path.resolve(__dirname, 'tpl_index.htm'), 'utf8', (err, data)=>{
if(err) this.err.push(err);
else this.tpl = data;
});
}
//set $menu_header with links for categories; Receives $ctgs = object with all categories
menuHeader(ctgs){
for(var prop in ctgs){
var href = (prop =='/') ?'' :prop+'/';
this.tpl_val.menu_header +='<a href="/'+href+'" title="'+ctgs[prop].title+'">'+ctgs[prop].link_txt+'</a>';
}
}
//set $menu_side with links of pages in current category - $pgs
menuSide(pgs){
var menu_side ='';
for(var prop in pgs){
var href = (this.ctg =='/') ?prop :this.ctg+'/'+prop;
menu_side +='<li><a href="/'+href+'.htm" title="'+pgs[prop].title+'">'+pgs[prop].link_txt+'</a></li>';
}
if(menu_side !='') this.tpl_val.menu_side ='<ul>'+menu_side+'</ul>';
}
//sets $pg $ctg with data from url; receives the request
setPgCtg(req){
var cp = url.parse(req.url, false, true).pathname.split('/'); //[categories, page]
this.pg = cp.pop().replace('.htm', '');
this.ctg = cp.join('');
if(this.ctg =='') this.ctg ='/';
}
//adds page data (title, description, content) in $tpl_val
pageData(){
//set name for file content
if(this.pg !='') var file =this.pg; //page in category
else if(this.ctg !='/' && this.pg =='') var file = this.ctg; //category
else var file ='index';
// sets title and description
if(this.pages[this.ctg]){
if(this.pg ==''){ //category
this.tpl_val.title = this.pages[this.ctg].title;
this.tpl_val.description = this.pages[this.ctg].description;
}
else if(this.pages[this.ctg].pages[this.pg]){ //page
this.tpl_val.title = this.pages[this.ctg].pages[this.pg].title;
this.tpl_val.description = this.pages[this.ctg].pages[this.pg].description;
}
}
this.tpl_val.content = fs.readFileSync(path.resolve(__dirname, 'content/'+file+'.htm'), 'utf8');
}
//call methods that sets data for current category and template; receives client request from server
set page(req){
this.setPgCtg(req);
if(this.pages[this.ctg] && this.pages[this.ctg].pages) this.menuSide(this.pages[this.ctg].pages);
this.pageData();
}
//return html page document
get page(){
//adds errors before content
if(this.err.length >0) this.tpl_val.content ='<div id="err">'+this.err.join('<br>')+'</div>'+this.tpl_val.content;
//parse $tpl_val to add data in $tpl; a={${str}}, b=str
var tpl_val = this.tpl_val;
var re = this.tpl.replace(/\{\$\{([^\}]+)\}\}/ig, function(a,b){ return tpl_val[b] ? tpl_val[b] :a; });
return re;
}
}
//assign object of setpage class to module.exports
module.exports = new setpage();
4. The main.js file for Node.js server
Then, we create a "
main.js" file in the "site1/" folder; this will be the Node.js server:
//include needed modules
const http = require('http');
global.fs = require('fs');
global.path = require('path');
global.url = require('url');
const setpage = require('./setpage');
//create the server
const server = http.createServer((req, res)=>{
res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'}); //adds header to display html content with utf-8
//call method that sets page data for current request
setpage.page = req;
//get and write html page content
var page = setpage.page;
res.write(page, 'utf-8');
res.end();
});
server.listen(8080, ()=> {
console.log('Server running at //localhost:8080/');
});
- Once all the files are created in the "site1/" directory, run the "
site1/main.js" file in command line interface to start the Node.js server:
node site1/main.js
Acces the website in your browser:
http://localhost:8080/
• For Demo, click:
Demo Node.js Simple WebSite.
• To download all the files of this project, click:
Get Node.js Simple WebSite.