The Complete Node.js Developer Course
Table of Contents generated with DocToc
- Introduction
- Node.js Fundamentals
- Asynchronous Programming
- Web Server and Application Deployment
- Testing Node Applications
- MongoDB, Mongoose and Rest API
- Handling File Uploads
- Realtime Applications
Introduction
Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
Official Course PDF Guide
Official Documentation
JavaScript is a single threaded programming language. However, node.js uses other threads using C++ behind the scenes to provide non-blocking capabilities such as handling callbacks and promises.
Node.js Fundamentals
- Requiring your own files
// uses relative paths const notes = require('./notes.js');
- Module
- available to all files
- node uses CommonJS modules
$ node > module Module { id: '<repl>', exports: {}, parent: undefined, filename: null, loaded: false, children: [], paths: [ '/Users/anuragkapur/tech-stuff/workspace/ak/anuragkapur.github.io/repl/node_modules', '/Users/anuragkapur/tech-stuff/workspace/ak/anuragkapur.github.io/node_modules', '/Users/anuragkapur/tech-stuff/workspace/ak/node_modules', '/Users/anuragkapur/tech-stuff/workspace/node_modules', '/Users/anuragkapur/tech-stuff/node_modules', '/Users/anuragkapur/node_modules', '/Users/node_modules', '/node_modules', '/Users/anuragkapur/.node_modules', '/Users/anuragkapur/.node_libraries', '/usr/local/lib/node' ] }
- Exporting to module
module.exports.age = 25;
module.exports.addNote = () => {
console.log('addNote');
}
add = (a, b) => a + b;
square = x => x * x;
module.exports = {
add,
square
};
- String formatting with variables (ES6)
console.log(`Sum of numbers = ${result}`);
- Initialise npm module
$ npm init
- Install npm module
```
npm install
// save in package.json - done by default in npm version >= 5.0
npm install
// install as a global utility - doesn’t add to the project specific package.json
npm install
// install as a dev dependency only
npm install
* node_modules directory shouldn't be committed to git
* nodemon - dev utility to automatically restart app when there are code changes
https://www.npmjs.com/package/nodemon
- nodemon - doesn't watch template (hbs) files by default, include them using
nodemon server.js -e js, hbs
* Accessing command line args
```javascript
process.argv
==
vs===
===
does not do type conversion before doing the comparison> if (2 == '2') console.log('equal'); else console.log('not'); equal undefined > if (2 === '2') console.log('equal'); else console.log('not'); not
-
yargs - utility module to handle command line arg parsing https://www.npmjs.com/package/yargs
- ES6 JSON variables can skip value if it is same as key
{ title: title, body: body }
is same as
{ title, body }
- Debugging via command line
node inspect <filename>
n
for nextc
for continuerepl
enter into repl at the current debug statedebugger;
add this to application code to add a break point
- Debugging via chrome dev tools
Add
debugger
statement to relevant line of code to act as a break point,node inspect <filename>
Next, go to chrome://inspect
- ES6 arrow functions don’t bind to the
this
keyword ```javascript const user = { name: ‘Anurag’, sayHi: () => { console.log(Hi, I'm ${this.name}
); } }
user.sayHi();
Prints:
Hi, I'm undefined
Alternate to the above:
```javascript
const user = {
name: 'Anurag',
sayHi: () => {
console.log(`Hi, I'm ${this.name}`);
},
sayHiAlt () {
console.log(`Hi, I'm ${this.name}`);
}
}
user.sayHiAlt();
- Object Destructuring
var user = {name: 'Anurag Kapur', location: 'London'}; var {name, location} = user;
- Working with time https://momentjs.com/
Asynchronous Programming
- All functions get added to call stack before execution
- Event loop pushes items from the Callback queue to the call stack, only when the call stack is empty
-
Promises provide clearer semantics and code readability compared to callbacks ```javascript // typical callback usage doWorkCallback((error, result) => { if (error) { return console.log(error); }
console.log(result); });
// typical promise usage doWOrkPromise.then((result) => { console.log(result); }).catch((error) => { console.log(error); });
* Promise chaining instead of nesting
```javascript
add(1, 2).then((sum) => {
console.log(sum); // Will print 3 return add(sum, 4)
return add(sum, 4);
}).then((sum2) => {
console.log(sum2); // Will print 7
}).catch((e) => {
console.log(e);
});
- Async-Await - syntactic sugar over promises
const doWork = async () => { const sum = await add(1, 99) const sum2 = await add(sum, 50) const sum3 = await add(sum2, 3) return sum3 } doWork().then((result) => { console.log('result', result) }).catch((e) => { console.log('e', e) })
Web Server and Application Deployment
https://github.com/anuragkapur/udemy-node-web-server contains example code using the following:
- Express
- Middleware is called in order of declaration in the code
- The files under
/public
are still rendered in the following code, even though we have setup the middleware to render a maintenance page in the code sample below: ```javascript const server = express();
hbs.registerPartials(__dirname + ‘/views/partials’); server.use(express.static(‘public’)); server.set(‘view engine’, ‘hbs’); // maintenance middleware server.use((req, res, next) => { res.render(‘maintenance.hbs’, { pageTitle: ‘Maintenance Page’ }); }); ``` To remedy this, simply move the call to
server.use(express.static('public'))
to after the maintenance middleware declaration. - The files under
- Middleware is called in order of declaration in the code
- Handlebars
- Partials for sharing code across html templates, example for header/footer code
- Helper functions
- Express Middleware
- Like servlet filters in java
app.use((req, res, next) => { // do something useful with the req and/or res next(); });
- Like servlet filters in java
Testing Node Applications
http://mochajs.org/
https://jestjs.io/
https://www.npmjs.com/package/supertest
https://github.com/anuragkapur/udemy-node-tests
- Using
nodemon
to run tests automatically when a file changesnodemon --exec 'npm test'
Or, add the following script to the
package.json
file"test-watch": "nodemon --exec \"npm test\""
and then in the bash shell,
$ npm run test-watch
-
Assertion library - Expect
- Testing async methods - using
done()
it('should square the number asynchronously', (done) => { utils.asyncSquare(3, (result) => { expect(result).toBe(9).toBeA('number'); done(); }) });
- Supertest - Express app testing library
it('should get users and the response should contain expected user in users array', (done) => { request(app) .get('/users') .expect(200) .expect((response) => { expect(response.body).toInclude({ name: 'Anurag Kapur', location: 'London' }) }) .end(done); });
-
Mocking node modules using jest - https://jestjs.io/docs/en/manual-mocks
- Spies for unit testing
- Rewire
```javascript const rewire = require(‘rewire’); const expect = require(‘expect’);
const app = rewire(‘./app’);
describe(‘App’, () => {
const db = { saveUser: expect.createSpy() }; app.__set__('db', db); it('should call saveUser with user object', () => { const email = 'anurag@example.com'; const password = 'password'; app.handleSignup(email, password); expect(db.saveUser).toHaveBeenCalledWith({email, password}); }); }); ```
- Rewire
MongoDB, Mongoose and Rest API
- MongoDB Node.js driver
- Object/Document relationships ```javascript const Todo = mongoose.model(‘Todo’, { description: { type: String, trim: true, required: true }, owner: { type: mongoose.Schema.Types.ObjectId, required: true, ref: ‘User’ } });
userSchema.virtual(‘todos’, { ref: ‘Todo’, localField: ‘_id’, foreignField: ‘owner’ });
user.populate(‘todos’).execPopulate();
* Add automatic timestamps to docs
```javascript
const todoSchema = new Schema(
{
description: {
type: String,
trim: true,
required: true
}
}, {
timestamps: true
}
);
const Todo = mongoose.model('Todo', todoSchema);
Handling File Uploads
- Express ecosystem library - Multer
- Sample code implemented - https://github.com/anuragkapur/udemy-node-todo-api/blob/106eec6ef6898a442ab4b85905b9e9fe9ea0183c/src/router/users.js#L81
Realtime Applications
https://github.com/anuragkapur/udemy-node-chat-app
-
On the client,
socket.emit
emits an event to the server. On the server, bothsocket.emit
andio.emit
can be used.socket.emit
sends an event to that specific client, whileio.emit
sends an event to all connected clients. - Events can be broadcasted from the server using
socket.broadcast.emit
. This event will get sent to all sockets except the one that broadcasted the event.io.on('connection', (socket) => { socket.broadcast.emit('message', 'A new user has joined!') })
- Socket.io rooms https://socket.io/docs/rooms-and-namespaces/#Rooms