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>nfor nextcfor continuereplenter into repl at the current debug statedebugger;add this to application code to add a break point
- Debugging via chrome dev tools
Add
debuggerstatement 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
thiskeyword ```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
/publicare 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
nodemonto run tests automatically when a file changesnodemon --exec 'npm test'Or, add the following script to the
package.jsonfile"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.emitemits an event to the server. On the server, bothsocket.emitandio.emitcan be used.socket.emitsends an event to that specific client, whileio.emitsends 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