part 1

Agenda

  • Whats this?

  • Tools from node

  • MongoDB

  • Our first route

  • Callbacks -> promises -> async/await

  • Templates

What are we building?

a company directory with skills

Repository


git clone git@github.com:goofyahead/NodeJS-crash-course.git

NodeJS

NodeJS

  • Event-driven

  • Single-thread

  • javascript

  • V8 engine -> ES6

  • Events - async APIS - non blocking IO

Non blocking IO

The event loop

V8

To the code!

Setup

mkdir myAwesomeApp
cd myAwesomeApp
#(OPEN OUR EDITOR WITH THE FOLDER)
npm init
https://github.com/creationix/nvm
//index.js

console.log('hello')
"scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

Run

npm start

Our first endpoint

http module

https://nodejs.org/api/http.html#http_http
const http = require('http')
const port = 8080

const server = http.createServer((req, res) => {
	res.end('hello from server')
})

server.listen(port, () => {
	console.log(`server running on ${port}`)
})

step-1

the libraries

npm install --save hapi
const Hapi = require('hapi');

const server = Hapi.server({
	port: 3000,
	host: 'localhost'
});

const init = async () => {
	await server.start();
	console.log(`Server running at: ${server.info.uri}`);
};

process.on('unhandledRejection', (err) => {
	console.log(err);
	process.exit(1);
});

init();

step-1

hapi routes

server.route({
    method: 'GET',
    path: '/',
    handler: (request) => {
        return 'Hello, world!';
    }
});

server.route({
    method: 'GET',
    path: '/person/{name}',
    handler: (request) => {
        return `Hello, ${encodeURIComponent(request.params.name)}!`;
    }
});

step-1

You are a backend dev now!

The database

MongoDB

mongod --config /usr/local/etc/mongod.conf
> mongo myDb


db.stats()
{
	"db" : "test",
	"collections" : 0,
	...
	"ok" : 1
}

db.people.insert({name: 'alex', skills: ['android','java','tortilla'],picture: ''})

db.people.find()
{ "_id" : ObjectId("5b16655b8e0185012b273dd8"), 
"name" : "alex", 
"skills" : [ "android", "java", "tortilla" ], 
"picture" : "" }

step-2

Access it from our code

npm install mongodb --save
const MongoClient = require('mongodb').MongoClient;

// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'myDb';

class Db {
	constructor (){
		MongoClient.connect(url, function(err, client) {
			console.log("Connected successfully to server");
			const db = client.db(dbName);
		});
	}

	getPeople(){
		return 'people!'
	}
}

module.exports = new Db()

step-2

Access it from our code

getPeople(callback){
	const collection = this.db.collection(collectionName)
        // Find some documents
	
	collection.find(whereClause).toArray(function(err, docs) {
		console.log("Found the following records")
		console.log(docs)
		callback(docs)
	})
}

step-2

server.route({
    method: 'GET',
    path: '/people',
    handler: (request) => {
        Db.getPeople((people) => {
            console.log(people)
            return (people)
        })
    }
})

What's this?

core.js:125 Debug: internal, implementation, error 
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (/Users/tsl057/github/myAwesomeApp/node_modules/hapi/lib/toolkit.js:52:29)
at process._tickCallback (internal/process/next_tick.js:68:7)

step-2

With promises

server.route({
    method: 'GET',
    path: '/people',
    handler: (request) => {
        return new Promise(function (resolve, reject) {
            Db.getPeople((people) => {
                console.log(people)
                resolve(people)
            })
        })
    }
})

step-3

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Callbacks
Promises
async/await

step-4

Callback

getPeople(callback){
	const collection = this.db.collection(collectionName)
        // Find some documents
	
	collection.find(whereClause).toArray(function(err, docs) {
		console.log("Found the following records")
		console.log(docs)
		callback(docs)
	})
}

step-4

Callback hell

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

step-4

Promises

const x = new Promise( function (resolve, reject) {
    // do something asynchronous
    reject(err)
    resolve(result)
})
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
http://bluebirdjs.com/docs/api/promise.promisifyall.html

step-4

async / await

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })

makeRequest()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
const makeRequest = async () => {
  console.log(await getJSON())
  return "done"
}

makeRequest()

step-4

async / await

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

step-4

async / await


const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

step-4

more than json

step-5

server side rendering

step-5

https://github.com/hapijs/vision

adding plugins

step-5

const init = async () => {
    await server.register(Vision);

    server.views({
        engines: { html: Handlebars },
        relativeTo: __dirname,
        path: `templates`
    });

    await server.start();
    console.log('Server is running at ' + server.info.uri);
};
const Hapi = require('hapi')
const Vision = require('vision')
const Handlebars = require('handlebars')

rendering

step-5

server.route({
    method: 'GET',
    path: '/render/people/{name}',
    handler: async (request, h) => {
        const person = await Db.getPerson(request.params.name)

        return h.view('person', {
            message: 'Hello Handlebars!',
            person: person
        });
    }
})

templates

step-5

<html>
<head title='{{message}}'>
	<body>
		<div>
			<h1>{{person.name}}</h1>
			<ul>
			{{#each person.skills}}
				<li>{{this}}</li>
			{{/each}}
			</ul>
			<img src='{{person.picture}}'/>
		</div>
	</body>
</html>

Questions?

Homework

  • Play around with this (promises and =>)
  • Understand module.exports
  • Allow insertion of people REST
  • Form to perform insert with body
  • Go to MDN and node website!

NodeJS part 1

By Alejandro Vidal Rodriguez

NodeJS part 1

  • 42