0%

Node.js Tutorial

Let’s Node.js!

node app.js

Global Object

https://nodejs.org/dist/latest-v6.x/docs/api/globals.html

1
2
3
4
5
6
7
8
// app.js
console.log(__dirname);
console.log(__filename);
/* output
/Users/Hsu/code/nodejs/practice
/Users/Hsu/code/nodejs/practice/app.js
*/

Modules and require()

https://nodejs.org/dist/latest-v6.x/docs/api/modules.html

EX1

1
2
3
4
5
6
// stuff.js
var counter = function (arr) {
return '有 ' + arr.length + ' 個元素在這個陣列。';
};
module.exports = counter; // return counter
1
2
3
4
5
6
7
8
// app.js
var you_can_use_other_name = require('./stuff');
console.log(you_can_use_other_name(['hi', 'bye', 'good']));
/*
有 3 個元素在這個陣列。
*/

EX2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// stuff.js
var counter = function (arr) {
return '有 ' + arr.length + ' 個元素在這個陣列。';
};
var adder = function (a, b) {
return `${a}${b}的總和是${a+b}。`;
}
var pi = 3.1415926535;
// module.exports.counter = counter;
// module.exports.adder = adder;
// module.exports.pi = pi;
module.exports = {
counter: counter,
adder: adder,
pi: pi
}
1
2
3
4
5
6
7
8
9
10
11
var stuff = require('./stuff');
console.log(stuff.counter(['hi', 'bye', 'good']));
console.log(stuff.adder(4, 1));
console.log(stuff.adder(stuff.pi, 1));
/*
有 3 個元素在這個陣列。
4與1的總和是5。
3.1415926535與1的總和是4.1415926535。
*/

Events

https://nodejs.org/dist/latest-v6.x/docs/api/events.html

EX1

1
2
3
4
5
6
7
8
9
10
// app.js
var events = require('events');
var myEmitter = new events.EventEmitter();
myEmitter.on('someEvent', function (msg) {
console.log(msg);
});
myEmitter.emit('someEvent', 'the event was emmitted');

EX2

https://nodejs.org/dist/latest-v6.x/docs/api/util.html#util_util_inherits_constructor_superconstructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// app.js
var events = require('events');
var util = require('util');
var Person = function (name) {
this.name = name;
};
util.inherits(Person, events.EventEmitter);
var james = new Person('james');
var mary = new Person('mary');
var ryu = new Person('ryu');
var people = [james, mary , ryu];
people.forEach(function (person) {
person.on('speak', function (msg) {
console.log(person.name + ' 說:' + msg);
})
})
james.emit('speak', '你好啊');

EX2-ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// app.js
const EventEmitter = require('events');
class Person extends EventEmitter {
constructor(name) {
super();
this.name = name;
}
}
const james = new Person('james');
const mary = new Person('mary');
const ryu = new Person('ryu');
let people = [james, mary , ryu];
people.forEach( (person) => {
person.on('speak', (msg) => {
console.log(person.name + ' 說:' + msg);
})
})
james.emit('speak', '你好啊');

File System

了解同步、非同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var fs = require('fs');
fs.readFile('README.md', 'utf-8', function(err, data) {
console.log("readFile");
});
console.log('end. of readFile');
var data2 = fs.readFileSync('README.md', 'utf-8');
console.log("readFileSync");
console.log('end. of readFileSync');
/*
end. of readFile
readFileSync
end. of readFileSync
readFile
*/

EX:讀寫檔案

1
2
3
4
5
6
7
8
9
10
11
// app.js
const fs = require('fs');
// 同步執行要一行一行很慢
// const readme = fs.readFileSync('README.md', 'utf8');
// fs.writeFileSync('WRITEME.md', readme);
// 異步執行可以達到 non-blocking
const readme = fs.readFile('README.md', 'utf8', function (err, data) {
fs.writeFile('WRITEME.md', data);
});

EX:資料夾、刪除檔案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// app.js
const fs = require('fs');
// delete file
// fs.unlink('WRITEME.md');
// fs.mkdirSync('stuff');
// fs.rmdirSync('stuff');
// create /stuff/WRITEME.md
fs.mkdir('stuff', function () {
fs.readFile('README.md', 'utf8' ,function (err, data) {
fs.writeFile('./stuff/WRITEME.md', data);
});
});
// delete /stuff/WRITEME.md
fs.unlink('./stuff/WRITEME.md', function () {
fs.rmdir('stuff');
});

Http

https://nodejs.org/dist/latest-v6.x/docs/api/http.html

1
2
3
4
5
6
7
8
9
10
11
const http = require('http');
var server = http.createServer(function (req, res) {
console.log('有人連進來囉~' + req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('HiHi');
});
server.listen(3000, 'localhost');
console.log('現在監聽著 Port 3000');

Stream

https://nodejs.org/dist/latest-v6.x/docs/api/stream.html

Read Stream

讀檔的時候可以一片段一片段的傳給使用者
https://nodejs.org/dist/latest-v6.x/docs/api/stream.html#stream_event_data

1
2
3
4
5
6
7
8
9
// app.js
const fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/README.md', 'utf8'); // 一個非常大的檔案
myReadStream.on('data', function (chunk) {
console.log('new chunk received');
// console.log(chunk);
})

Write Stream

https://nodejs.org/dist/latest-v6.x/docs/api/stream.html#stream_writable_streams

1
2
3
4
5
6
7
8
9
10
// app.js
const fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/README.md', 'utf8');
var myWriteStream = fs.createWriteStream(__dirname + '/WRITEME.md');
myReadStream.on('data', function (chunk) {
console.log('new chunk received');
myWriteStream.write(chunk);
})

Write Stream: Pipe

https://nodejs.org/dist/latest-v6.x/docs/api/stream.html#stream_readable_pipe_destination_options

1
2
3
4
5
6
7
// app.js
const fs = require('fs');
var myReadStream = fs.createReadStream(__dirname + '/README.md', 'utf8');
var myWriteStream = fs.createWriteStream(__dirname + '/WRITEME.md');
myReadStream.pipe(myWriteStream);

Http + Stream

Read File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app.js
const http = require('http');
const fs = require('fs');
var server = http.createServer(function (req, res) { // response is Writable Stream
console.log('有人連進來囉~' + req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
var myReadStream = fs.createReadStream(__dirname + '/README.md', 'utf8');
myReadStream.pipe(res);
});
server.listen(3000, 'localhost');
console.log('現在監聽著 Port 3000');

Read HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app.js
const http = require('http');
const fs = require('fs');
var server = http.createServer(function (req, res) {
console.log('有人連進來囉~' + req.url);
res.writeHead(200, { 'Content-Type': 'text/html' }); // 改為 text/html !!
var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8');
myReadStream.pipe(res);
});
server.listen(3000, 'localhost');
console.log('現在監聽著 Port 3000');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
body {
background: skyblue;
color: #fff;
padding: 30px;
}
.text-center {
text-align: center;
}
</style>
</head>
<body>
<h1 class="text-center">Aha</h1>
</body>
</html>

綜合應用

Return JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app.js
const http = require('http');
const fs = require('fs');
var server = http.createServer(function (req, res) {
console.log('有人連進來囉~' + req.url);
res.writeHead(200, { 'Content-Type': 'application/json' });
var myObj = {
name: 'QQ',
job: 'Sing'
};
res.end(JSON.stringify(myObj));
});
server.listen(3000, 'localhost');
console.log('現在監聽著 Port 3000');

Routing 原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// app.js
const http = require('http');
const fs = require('fs');
var server = http.createServer(function (req, res) {
console.log('有人連進來囉~' + req.url);
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
fs.createReadStream(__dirname + '/index.html').pipe(res);
} else if (req.url === '/api/goods') {
res.writeHead(200, { 'Content-Type': 'application/json' });
var myObj = { name: 'QQ' };
res.end(JSON.stringify(myObj));
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('404 Not Found');
}
});
server.listen(3000, 'localhost');
console.log('現在監聽著 Port 3000');

MongoDB 相關

robomongo(GUI)

https://robomongo.org
開源、跨平台

Mongoose

https://www.npmjs.com/package/mongoose
http://mongoosejs.com/

mLab

https://mlab.com

express

https://www.npmjs.com/package/express
https://expressjs.com

Init

npm init
npm install express -S

1
2
3
4
5
6
7
8
9
10
// app.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('HiHi');
});
app.listen(3000);

nodemon app.js

SendFile

1
2
3
4
5
6
7
8
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(3000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- /index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
body {
background: skyblue;
color: #fff;
padding: 30px;
}
.text-center {
text-align: center;
}
</style>
</head>
<body>
<h1 class="text-center">Aha</h1>
</body>
</html>

Route Params

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/profile/:id', function (req, res) {
res.send('HiHi ' + req.params.id);
});
app.listen(3000);

Template Engines: ejs

https://expressjs.com/en/4x/api.html#res.sendFile

npm install ejs -S

傳參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// app.js
var express = require('express');
var app = express();
app.set('view engine', 'ejs'); // add me
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/profile/:id', function (req, res) {
let data = { name: 'QQQ', age: 99, array: ['A', 'B', 'C'] };
let array = [ { name: 'Q'}, { name: 'QQ'}, { name: 'QQQ'} ];
res.render('profile', { id: req.params.id, data: data, array: array });
});
app.listen(3000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- views/profile.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
body {
background: skyblue;
color: #fff;
padding: 30px;
}
.text-center {
text-align: center;
}
</style>
</head>
<body>
<h1 class="text-center">Welcome! <%= id %></h1>
<p>Name: <%= data.name %></p>
<p>Age: <%= data.age %></p>
<ul>
<% data.array.forEach(item => { %>
<li><%= item %></li>
<% }); %>
</ul>
<ul>
<% array.forEach(item => { %>
<li><%= item.name %></li>
<% }); %>
</ul>
</body>
</html>

Partial Templates

1
<% include partials/nav.ejs %>
1
2
3
4
5
6
7
<!-- views/partials/nav.ejs -->
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/profile/QQQ">Profile</a></li>
</ul>
</nav>

Middleware

1
2
3
4
5
// app.js
app.use('/assets', function (req, res, next) {
console.log(req.url);
next();
})

Static Files

https://expressjs.com/en/starter/static-files.html

link directory /assets

1
2
// app.js
app.use('/assets', express.static('assets'));

Query Strings

https://expressjs.com/en/4x/api.html#req.query

1
2
3
app.get('/query', function (req, res) {
res.render('query', { qs: req.query } );
});

http://localhost:3000/query?qq=qqqqqqqqqqqq

1
<%= qs.qq %>

POST REQUEST

https://expressjs.com/en/4x/api.html#req.body
https://www.npmjs.com/package/body-parser

npm install body-parser

1
2
3
4
5
6
7
8
9
// app.js
var bodyParser = require('body-parser')
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.post('/query', urlencodedParser, function (req, res) {
console.log(req.body);
res.end('post-success ' + JSON.stringify(req.body));
});

Express Generate

npm install express-generator -g
express -h

Express Todo List

npm init
npm install express --save
npm install ejs --save
npm install body-parser --save
npm install mongoose --save

https://github.com/lovenery/express-todo

very brief and basic api format example.

NPM

https://www.npmjs.com

nodemon

https://www.npmjs.com/package/nodemon

開發階段可以不重開node.js
npm install -g nodemon

morgan

logger
npm install morgan -S

1
2
3
// express
var morgan = require('morgan');
app.use(morgan('dev'));

https://www.npmjs.com/package/cookie-parser
https://www.npmjs.com/package/express-session

passport.js

http://passportjs.org/

Facobook Login

https://github.com/jaredhanson/passport-facebook

For API

https://github.com/jaredhanson/passport-http-bearer

express-message

快閃資料
https://github.com/expressjs/express-messages

bcrypt

https://www.npmjs.com/package/bcrypt

connect-mongo

MongoDB session store for Express and Connect
https://github.com/jdesboeufs/connect-mongo

pm2, forever

https://github.com/Unitech/pm2

sudo npm install pm2 -g
pm2 start index.js
pm2 stop index.js

需要sudo來使用1024以下之port
sudo npm install pm2 -g
sudo pm2 list
sudo pm2 start index.js
sudo pm2 stop index.js
sudo pm2 delete index.js
sudo pm2 startup
sudo pm2 unstartup

Debuger

budo (client side)

https://github.com/mattdesl/budo

node-inspector (server side)

https://github.com/node-inspector/node-inspector

內建的 不太好用

node debug xxxx.js

還在實驗階段

node --inspect xxxx.js