We will create a diary app for you to keep track of your sanity before this course drives you into madness your great SE experience so that your children may hear of your great conquest and honer th sous of our fallen comrades.
While working on the app we will demonstrate a good git work flow.
By the end of the lab you should have acquired.
- An understanding of (node package manager) npm
- node module syntax (require/export)
- npm install
- Create package.json
- read package.json
- npm install from package and adding dependency
- npm test
- npm run [script]
- Build a simple express app
- Serve static files
- load some javascript and some css as separate files
- Update our interface with jQuery
- Update our interface data from a json file
- Build a simple API
- Query The API with jQuery and update our page
- Internet
- Google chrome
- A plain text editor (preferably sublime)
- mongodb
- nodejs
- git
- npm packges mocha, chai, supertest istanbul
- If you haven't yet, fill this form while creating a github account
- Lab 2 is a prerequisite for this lab assuming you cloned the se-tutorial repo you can redo it by
$ git checkout lab-2-start
To follow along this tutorial just
$ git checkout lab-4-start
The nodeJS eco-system has an abundance of packages that make carrying work forward easier and they are all stored on npm.
npm allows developers to share code across projects. We bundle this code in modules that we call packages.
To learn more about npm see their getting started guide. most notably there's a short npm install locally and globally, more on install syntax here.
All package related information about our app is noted in package.json
Since the package philosophy is based on making every package as small and reusable as possible, npm packages often have other packages dependencies.
For more on this see this section of the npm getting started guide.
There are many ways to start a project, there are many project generators that you can use similar that will create some files as your starting point.
In the previous version of this lab we had an external tutorial on building a restful app that used the express generator.
We will also use a generator later on in the future but for now we will walk you though creating your project manually.
Create a folder for our project (diary for example) and cd
into it
$ mkdir sanity-diary && cd sanity-diary
open sublime inside this folder
$ subl .
Create a minimal package.json file as described here either through the command or by writing it yourself.
You should wind up with something that looks like this
{
"name": "diary",
"version": "0.0.1",
"author": "Amr Draz <[email protected]>"
}
If you remember in lab-2 we constructed a basic node server
var http = require('http');
var handleRequest = function handleRequest(request, response){
response.writeHeader(200, {'Content-type':'text/plain'});
response.end('Server is working you visited: ' + request.url);
}
}
var server = http.createServer(handleRequest);
var PORT = 8080;
server.listen(PORT, function(){
console.log("Server listening on: http://localhost:%s", PORT);
});
And when we want to serve html files, we would do something like this
var http = require('http');
var fs = require('fs');
var handleRequest = function handleRequest(request, response){
if (request.url==='/index.html') {
response.writeHeader(200, {'Content-type':'text/html'});
response.end(fs.readFileSync('./app/index.html'));
} else {
response.writeHeader(404, {'Content-type':'text/html'});
response.end(fs.readFileSync('./app/404.html'));
}
};
var server = http.createServer(handleRequest);
var PORT = 8080;
server.listen(PORT, function(){
console.log("Server listening on: http://localhost:%s", PORT);
});
However as the node community recognizes, this architecture would eventually not scale very well, this is where middleware came in.
ExpressJS is a simple micro-framework that is highly expandable.
We will be using express in our project, so we will add it as a dependency.
If you understand that express is a package and how npm install works you should be able to pick the correct command form the express install guide. You will need the Internet at this point.
When you're done your package.json should look like this (at the time of writing this tutorial).
{
"name": "diary",
"version": "0.0.1",
"author": "Amr Draz <[email protected]>",
"dependencies": {
"express": "^4.13.4"
}
}
and your diary app folder should look like this
/Users/draz/se-project/se-tutorial/diary
|-- node_modules
`-- package.json
Now write a hello world express app and run it to try it out, change the port to 8080 so that we're consistent with those following along on c9.io.
If you follow along you should be capable of testing your server by running
$ node app.js
and visiting http://localhost:8080 in the browser. (on c9 hit preview)
You should now have a directory structure that looks like this
/Users/draz/se-project/se-tutorial/diary
|-- app.js
|-- node_modules
`-- package.json
It is always best to abstract away unneeded development details from other people.
We could type node app.js
every time we want to start the app, but what if we decide to do things differently later on like use nodemon instead of node or other start commands.
Better to setup a facade in front of the app starting logic.
We will add a start script to our package.json in order to unify our app start command (also the command may become longer later).
Your package.json should now look something like this.
{
"name": "diary",
"version": "0.0.1",
"author": "Amr Draz <[email protected]>",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.13.4"
}
}
and now we can start the app by running
$ npm start
You can learn more about express from their website guide and api refrence when you need too.
In lab-1 we created a simple google.com page and in it we wrote all our html in addition to the css code.
<!DOCTYPE html>
<html>
<head>
<title>Google</title>
<style>
body {
text-align: center;
font-family: Arial;
}
.search {
width: 400px;
}
</style>
</head>
<body>
<h1>Google</h1>
<form action="http://www.google.com" method="get">
<input class='search' type="text" placeholder="Search" name="q">
<div>
<input type="submit" value="Search">
<input type="submit" value="I'm feeling luck">
</div>
</form>
</body>
</html>
Create a folder called static in your project and add an html file called index.html with the code above
You can then update the app.js file as follows
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.sendFile(__dirname + '/static/index.html');
});
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
To understand how sendFile()
works see the express api reference
Note: Make it a habit to look up things you don't know.
Your directory should now look like this
|-- app.js
|-- node_modules
|-- package.json
`-- static
`-- index.html
Start the app and test that everything is working
$ npm start
remember to test early and test often
A static resource page is a page that is served as is and was not generated pragmatically.
Examples of these resources are html, css, javascript, images, video, and audio files to name a few.
In practice it is generally better to not embed css and javascript code in your html page, and instead split them to separate files and folders.
We will start implementing our hip diary app so let's change index.html.
<!DOCTYPE html>
<html>
<head>
<title>My Diary</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" />
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<header class="header">
<h1>Thoughts for my sanity</h1>
</header>
<section class="post-list">
<article class="post-list-item">
<h3 class="post-list-item-header">This is an example of a post</h3>
<section class="post-list-item-body">
We will populate this with ajax later but sometimes it is better to start off designing statistically before we go dynamic
</section>
</article>
</section>
<script src="js/main.js"></script>
</body>
</html>
The <link>
and <script>
tags are used to refer to external css and javascript files respectively.
You should:-
- Add a
css
andjs
folder to your static file directory. - Add a
style.css
empty file in your css folder - Add a
main.js
empty file in your js folder
You can open the index.html file in your browser by double clicking on the file, dragging it to the browser.
You should see something like this
If you're wondering what normalize.css does
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" />
Then you're asking the right questions and I salute your constructive curiosity, you can find out by goggling of course.
You might also notice we're not referring to it from our local file system but from another website.
This will help illustrate a point later.
Try removing the nomralize.css link tag to see how the site looks without it
Now put it back and add the following to your style.css
file
* {box-sizing: border-box;}
html {
height: 100%;
}
body {
min-height: 100%;
color: #222;
line-height: 1.5;
background: #eee;
}
.header {
height: 500px;
display: flex;
align-items: center;
justify-content: center;
background-color: #345;
/* https://css-tricks.com/perfect-full-page-background-image/ */
background: url(../img/header.jpeg) no-repeat center center fixed;
background-size: cover;
margin-bottom: 10px;
}
.header h1 {
max-width: 50%;
color: #fff;
text-shadow: 2px 2px rgba(0, 0, 0, 0.3);
font-size: 4em;
font-weight: 200;
font-family: cursive;
}
.post-list {
margin: 0 auto;
}
.post-list-item {
overflow:auto;
margin: 10px auto;
width: 50%;
box-shadow: 1px 1px 5px rgba(49, 21, 4, 0.4);
min-height: 140px;
padding: 20px 20px;
}
.post-list-item-header {
padding: 0 0 10px 10px;
font-weight: 200;
font-size: 2em;
border-bottom: 1px solid rgba(0,0,0,0.3);
margin-top: 10px;
}
.post-list-item-body {
padding: 0 10px;
}
The image in the background is found in the repo under
assets/diary-app/header.jpeg
If you refresh your web page right now you should now see something like this.
If we now go back to our terminal, run our server and visit http://localhost:8080
we will see the page unstyled.
But you may notice that normalize is still active, this is because it is served from another server, the problem has to do with our server.
You can tell if there are errors in the browser by opening the developer tools and looking in the console tab (right click and inspect element)
You should see that the server is responding with a 404 in the networks tab
This is because we only told express to serve a file when we visit /
and no other route was defined.
We could add this to our app.js
app.get('/css/style.css', function(req, res) {
res.sendFile(__dirname + '/static/css/style.css');
});
But it is unreasonable to have to define a new route for every new file we want to serve.
Instead we can expose a static directory
You might want to read about how the express routes work and how you can set a static folder in express
In fact since our index.html is static file we no longer need to define its route. your app.js should look like this.
var express = require('express');
var app = express();
app.use(express.static('./static'));
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
Restart your server and refresh your webpage everything should now look dandy.
We This have created a simple static server now everything you put in your static folder will be accessible through your site.
It is possible to manipulate our web page using JavaScript.
For instance we can select our post header and change its text like so.
Add this to your main.js file
document.querySelector('.post-list-item-header').innerHtml = "Title added with javascript";
If you're thinking I don't understand what document.querySelector means or innerHTML, Then congratulations once again you are right you shouldn't maybe goggling what they are may enlighten you.
Now how about you add to your javascript file some code that will select the body of the post and update it to say something like "This body text was also modified with javascript"
.
We can make our code look smaller along with simplifying some functionality later by using JQuery.
First You will need to add jQuery to our webpage I'll leave it up to you to figure that one out.
In case you still didn't click this link for shame
Note: You should add it before our main.js script since it depend on it.
Your main.js should now look like this
$('.post-list-item-header').html("Title added with JavaScript");
$('.post-list-item-body').html("This post's body text was populated with javascript too");
Preview your webapp in the browser to make sure everything works.
In case something is wrong just open the browser console and see if there are any errors and resolve them, Google is your friend.
Right now our post is updated immediately on page load. We can change that by having it update only when a certain event is triggered.
We will add a button to our page just before our post-list
section.
<button class="post-load-btn">Load Post</button>
Add this to your styles.css
file
.post-load-btn {
margin: 0 auto;
display: block;
cursor: pointer;
width: 50%;
font-size: 2em;
background: #3C3C3C;
color: #fff;
border: none;
box-shadow: 1px 1px 5px rgba(49, 21, 4, 0.4);
}
.post-load-btn:hover {
background-color: #555;
}
Now in our main.js add a click event that updates the post on click.
// main.js
$('.post-load-btn').on('click', function (event) {
$('.post-list-item-header').html("Title added with JavaScript");
$('.post-list-item-body').html("This post's body text was populated with javascript too");
});
It is possible to trace your code by using
console.log()
to check that everything is working fine.
Test your code, check the browser console for errors.
It is generally a good idea to model our data which makes it easier to scale our code.
We will clean up main.js to look like this
// main.js
var post = {
"title": "Title added with JavaScript",
"content": "This post's body text was populated with JavaScript too"
};
$('.post-load-btn').on('click', function (event) {
$('.post-list-item-header').html(post.title);
$('.post-list-item-body').html(post.content);
});
Now that we abstracted away our data from our code we can think of different ways of getting the data.
When populating our post data we may want to load this data form somewhere else, say a database for example.
To do this we use a technology called AJAX which allows us to load data asynchronously without reloading the page.
So first let's take out our post data and move it to another file say post.json
Add a post.json file to our static folder
{
"title": "Title added with Ajax from a JSON file",
"content": "This post's body text was populated with JavaScript"
}
And now let's get it with AJAX
//main.js
$('.post-load-btn').on('click', function (event) {
$.ajax({
url: 'post.json',
success: function (post) {
$('.post-list-item-header').html(post.title);
$('.post-list-item-body').html(post.content);
}
});
});
Note: You don't need jQuery to do ajax calls jQuery just provides a nice syntax for executing ajax requests that's all.
Note: just like we loaded a json file with ajax we can load a css, html, text, javascript or any other file.
The data we load may not be static like our html and css files.
In practice our data will probably be loaded from a database or file and modified later.
This is where we need to define routes in our express app to handle these various cases.
Let's add a get route /api/post
which returns a json response to our app.js
// app.js
app.get('/api/post', function(req, res) {
var post = {
"title": "Title added with Ajax from a /api/post route",
"content": "This post's body text was populated with JavaScript"
}
res.send(post)
});
And now we can update our main.js file to communicate withe the get api route instead.
// main.js
$('.post-load-btn').on('click', function (event) {
$.ajax({
url: 'api/post',
success: function (post) {
$('.post-list-item-header').html(post.title);
$('.post-list-item-body').html(post.content);
}
});
})
If you get a 404
in your console then you forgot to restart the server.
Now that we can send requests and know we can receive a response from the server, we can get our data however we please, be it from database or file.
If you recall from lab-2 we showed you how to connect to mongodb with nodejs you can also read the mongodb npm documentation to learn how to do that as well
If you got lost you can
$ git checkout lab-4-end
Which will contain final code for this lesson.
Writing tests is essential for long term project maintenance.
In large system this may not be so apparent and our only means of avoiding this is to write code that tests our code.
You're used to doing it yourself, you probably wrote print statements in your code when solving an algorithm to see if it's working fine, well my friend let me greet you with a wide smile for you have a little software tester in you.
Complete the following tutorials
And add a test file to test our one api route.
-
You can try other tutorials to get a fresh perspective restful app just Google
node rest app tutorial
or any other variations.- In the tutorial the author does
npm update -g express
and then laternpm update -g express-generator
. It should benpm install -g express-generator
. You may also encounter an issue with the package.json file provided you need to change the mongodb version in it to ~1.4 instead of 2.0.1.
- In the tutorial the author does
-
Learn JQuery
- the folks on w3schools have a jQuery reference.
- see try jQuery course should take 3 hours to fully complete.