koajs as an alternative to express - odessajs'16

31
Koajs as an alternative to Express

Upload: -

Post on 12-Apr-2017

265 views

Category:

Internet


0 download

TRANSCRIPT

Koajs as an alternative to Express

I am Mykola Kozhuharenkofront-end dev in Krusche & CompanyI’m on github

Hello!

Nodejs/Express downsides:

◉Code readabilityCallback hell

◉Error handlingDuplicate callbacks / cb call might be lost in

third party libraryIf you miss an error handler - you are in

troublesCan't throw an error or use try/catch

app.post('/',function (req, res, next) { var body = req.body; if (!body.name || !body.email || !body.password) { return res.status(400).send("Missing username or email or password") }; // case #1- if user was registered (socials platform) before -> just add a password; User.findOneAndUpdate({ email: body.email }, { $set: {password: body.password} }, (err, user) => { if (!user) { // case #2 - user has never been registering before -> create new one; User.create(body, (err, user) => { if (err && err.name === 'ValidationError') { return res.status(400).send(err) } else if (err) { return res.status(500).send(err) } return res.send({ user: user }) }); } else { return res.send({ user: body }) } });});

Callbacks

User.findOneAndUpdate({ email: body.email }, { $set: {password: body.password} }, (err, user) => { if (err) { return res.status(500).send(err) } if (!user) { // case #2 - user has never been registering before -> create new one; User.create(body, (err, user) => { if (err && err.name === 'ValidationError') { return res.status(400).send(err) } else if (err) { return res.status(500).send(err) } return res.send({ user: user }) }); } else { return res.send({ user: body }) } });

Callbacks

◉ If you miss an error handler - you are in troubles

◉ Duplicate callbacks / cb call might be lost in third party library

// foo is a function that returns a generatorfunction *foo() { console.log('start'); yield 1; console.log('second'); yield 2; yield 3;}

var gen = foo(); // get a generator objectgen.next() // 'start', { value: 1, done:false }gen.next() // 'second', { value: 2, done:false }gen.next() // { value: 3, done:false }gen.next() // { value: undefined, done:true }

Generators #1

const Message = require('./models/message');

function *foo() { yield new Promise((resolve, reject) => { resolve("Hello") }); yield Message.create({text: 'some text');}

var gen = foo();var genVal = gen.next().value // { value: Promise, done:false }genVal.then(res => console.log(res)) // Hellogen.next() // { value: Promise, done:false }

Generators #2 - Promises

const Message = require('./models/message');

function *foo() { var greeting = yield new Promise.resolve('Hello'); var result = yield Message.create({text: greeting}); console.log(result) // saved! {id: 'ID', text: 'Hello'}}

var gen = foo();gen.next().value // Promise .then(greeting => { console.log(greeting); // 'Hello' gen.next(greeting).value // Promise .then(createRes = gen.next(createRes)) })

Generators #3 - Two ways

var fetch = require('isomorphic-fetch');

function *foo() { try { var greeting = yield fetch('/api/get-greetings')[0]; } catch (e) { console.log(e); }}var gen = foo();gen.next().value .then(greeting => gen.next(greeting)) .catch(e => gen.throw(e))

Generators #4 - throw

function runner (gen) { var gen = gen(); next(null, null); function next(err, res) { const res = err ? gen.throw(err) : gen.next(res); if (res.done) return; res.value .then((res) => next(null, res)) .catch((error) => next(error)); }};

runner(function *foo() { var greeting = yield Promise.resolve("Hello") var name = yield Promise.resolve("John") console.log(greeting, name); // 'Hello John'})

Generators #5 - Runner

var co = require('co')

app.post('/',function (req, res, next) { var body = req.body; co(function *() { try { var user = yield User.findOneAndUpdate({ email: body.email }, { $set: {password: body.password} });

if (!user) { user = yield User.create(body); } res.send({ user: user }) } catch (e) { if (err && err.name === 'ValidationError') { return res.status(400).send(err) } else if (err) { return res.status(500).send(err) } } });});

Co

Using Co you can yield:

Thunks (will be deprecated)

Arrays

co(function* () { // parallel

var res = yield [ Promise.resolve(1),

Promise.resolve(2),

Promise.resolve(3), ];

console.log(res); // => [1, 2, 3]}).catch(onerror);

Generatorsfunction* greet() { return yield new Promise((resolve, rej) => { resolve("Hello, Mary!") });}

co(function *(){ var greeting = yield greet console.log(greeting)}).catch((err) => { console.log(err)});

Promises

Along with co will be useful:◉mz - promisify NodeJS core modules

◉Thenify promisify any callback-based function

var fs = require('mz/fs')

fs.exists(__filename).then(function (exists) { if (exists) // do something})

var thenify = require('thenify');

var somethingAsync = thenify(function somethingAsync(a, b, c, callback) { callback(null, a, b, c);});

Adopted by community

Callbacks Async Promise

sGenerato

rs(along with

co)

Meet Koa! Philosophically, Koa aims to "fix and

replace node", whereas Express "augments node"

Koa Hello world

var koa = require('koa');var app = koa();

app.use(function *() { this.body = 'Hello World';});

app.listen(3000);

Koa API

koa Request

CONTEXTthis.requestthis.response

koa Response

this.querythis.urlthis.headersthis.get - get a header

this.bodythis.type - get content-typethis.redirect this.statusthis.set -set a header

app.use(function *(next) { var start = new Date; // <<<------ 1

yield next; // waits for downstream middlewares

var delta = Math.ceil(Date.now() - start); // <<<------ 3 console.info(`${this.method} ${this.url} - ${delta}ms`); this.set('X-Response-Time', delta + 'ms'); // <<<------ 4});

app.use(function *() {this.body = yield new Promise.resolve([10000]); // <<<------ 2

});

Response time - Koa middleware

3

4

2

1

Custom error

app.use(function *(next) { try { yield next; } catch (e) { if (e instanceof AuthError) { this.statusCode = e.status; this.redirect('/not-authorized'); this.body = this.render('not-authorized'); } else if (e.status) { this.statusCode = e.status; this.body = this.render('error', e); } else { this.statusCode = 500; this.body = this.render('error', e); logger.error('Unexpected error: ', e); // winston } }});

app.use(function *(next) { throw new AuthError()});

app.post('/',function (req, res, next) { var body = req.body; if (!body.name || !body.email || !body.password) { return res.status(400).send("Missing username or email or password") }; // case #1- if user was registered (socials platform) before -> just add a password; User.findOneAndUpdate({ email: body.email }, { $set: {password: body.password} }, (err, user) => { if (err) return res.status(500).send(err) if (!user) { // case #2 - user has never been registering before -> create new one; User.create(body, (err, user) => { if (err && err.name === 'ValidationError') { return res.status(400).send(err) } else if (err) { return res.status(500).send(err) } return res.send({ user: user }) }); } else { return res.send({ user: body }) } });});

Do you remember this example with callbacks?

app.use(function *(next) { try { yield next; } catch (e) { if (e.status) { // app error this.body = e.message; this.statusCode = e.status; } if else (e && e.name === 'ValidationError') { this.body = e; this.statusCode = 400; } else { // Internal server error this.body = "Oops!"; this.status = 500; } }});

Koa way

app.use(function *(next) { var body = this.request.body;

if (!body.name || !body.email || !body.password) { throw new Error("Missing username||email||password"); }

var user = yield User.findOneAndUpdate({email: body.email}, { $set: {password: body.password} });

if (!user) user = yield User.create(body); this.body = { user: this.body };});

2nd middleware

1st middleware

app.get('/', function (req, res, next) { var readStream = fs.createReadStream('text.txt'); readStream.pipe(res); readStream.on('error', () => { res.statusCode = 500; res.end('Server error has occurred!'); }); res.on('close', () => readStream.destroy());});

Send a file

app.use(function* (next) { this.body = fs.createReadStream('text.txt')});

Express/node

Koa

“Koa and express uses the same modules. We’ve refactored express

into jshttp and pillarjs so that anyone else can make their own framework as

well”

Express & Koa similarities

Same modulesAccepts, content-type, cookie, fresh, content-disposition, on-finished, parseurl, type-is, vary, escape-html

ModularEach feature is moved to a separate module.

MinimalisticContains only essential (core) modules (e.g. headers parsing).

Same developersKoa was build by core developers of Express: TJ Holowaychuk and others

Koa

◉ router◉ bigger community◉ needs domains to catch

async errors◉ close to node style◉ a lot of outdated

examples and tutorials

Expressvs

◉ better error handling with try catch

◉ nice async flow control (no cb hell)

◉ api is more stable;◉ no build-in router◉ smaller community◉ support stream out of the

box◉ easy to understand source

code; no monkey patching

Any questions ?

Thanks!