Browse Source

Refactor koa (#3433)

* refactoring koa

* fix sequelize-postgres

* make database-layer optional

* fix benchmark config

* fix wrong middleware
add getting from db as plain objects
remove __v key for mongoose

* fixing database layer
(wtf mongo use lowercase table name)

* minify back fortunes view

* postgres fix
maximelkin 7 years ago
parent
commit
4d916064d1

+ 1 - 0
frameworks/JavaScript/koa/.gitignore

@@ -0,0 +1 @@
+node_modules/

+ 2 - 2
frameworks/JavaScript/koa/app.js

@@ -7,8 +7,8 @@ if (cluster.isMaster) {
     cluster.fork();
   }
 
-  console.log('Master starting ' + new Date().toISOString(" "));
-  cluster.on('exit', (worker, code, signal) => {
+  console.log('Master starting ' + new Date().toISOString());
+  cluster.on('exit', () => {
     process.exit(1);
   });
 } else {

+ 68 - 66
frameworks/JavaScript/koa/benchmark_config.json

@@ -1,70 +1,72 @@
 {
   "framework": "koa",
-  "tests": [{
-    "default": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MongoDB",
-      "framework": "koa",
-      "language": "JavaScript",
-      "flavor": "NodeJS",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "koa",
-      "notes": "",
-      "versus": "nodejs"
-    },
-    "mysql": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "koa",
-      "language": "JavaScript",
-      "flavor": "NodeJS",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "koa",
-      "notes": "",
-      "versus": "nodejs"
-    },
-    "postgres": {
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "Postgres",
-      "framework": "koa",
-      "language": "JavaScript",
-      "flavor": "NodeJS",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "koa",
-      "notes": "",
-      "versus": "nodejs"
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MongoDB",
+        "framework": "koa",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "koa",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "mysql": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "koa",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "koa",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "postgres": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "koa",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "koa",
+        "notes": "",
+        "versus": "nodejs"
+      }
     }
-  }]
+  ]
 }

+ 34 - 29
frameworks/JavaScript/koa/create-server.js

@@ -3,40 +3,45 @@ const Router = require('koa-router');
 const hbs = require('koa-hbs');
 const bodyParser = require('koa-bodyparser');
 const handlebars = require('handlebars');
-
-const Handler = require(`./handlers/${process.env.NODE_HANDLER}`);
+const handler = require('./handlers/handler');
 
 const app = new Koa();
 const router = new Router();
-app.use(bodyParser());
-app.use(hbs.middleware({
-  handlebars: handlebars,
-  viewPath: __dirname + '/views'
-}));
-
-function JsonSerialization(ctx, next) {
-  ctx.set('Server', 'Koa');
-  ctx.type = 'application/json';
-  ctx.body = { message: 'Hello, World!' };
-  return next();
-}
 
-function Plaintext(ctx, next) {
-  ctx.set('Server', 'Koa');
-  ctx.type = 'text/plain';
-  ctx.body = 'Hello, World!';
-  return next();
-}
+app
+  .use(async (ctx, next) => {
+    ctx.set('Server', 'Koa');
+    await next();
+  });
+
+router
+  .get('/json', (ctx) => {
+    ctx.body = {message: 'Hello, World!'};
+  })
+  .get('/plaintext', (ctx) => {
+    ctx.body = 'Hello, World!';
+  });
 
-router.get('/json', JsonSerialization);
-router.get('/plaintext', Plaintext);
-router.get('/db', Handler.SingleQuery);
-router.get('/queries', Handler.MultipleQueries);
-router.get('/fortunes', Handler.Fortunes);
-router.get('/updates', Handler.Updates);
+const handlerName = process.env.NODE_HANDLER;
 
+if (handlerName) {
+  const dbLayer = require(`./handlers/${handlerName}`);
+
+  const routerHandler = handler(dbLayer);
+
+  router
+    .use(bodyParser())
+    .use(hbs.middleware({
+      handlebars: handlebars,
+      viewPath: __dirname + '/views'
+    }))
+    .get('/db', routerHandler.SingleQuery)
+    .get('/queries', routerHandler.MultipleQueries)
+    .get('/fortunes', routerHandler.Fortunes)
+    .get('/updates', routerHandler.Updates);
+}
 
 app.use(router.routes());
-const server = app.listen(8080);
-console.log('Worker started and listening on http://0.0.0.0:8080 ' 
-  + new Date().toISOString(" "));
+
+app.listen(8080);
+console.log(`Worker started and listening on http://0.0.0.0:8080 ${new Date().toISOString()}`);

+ 49 - 0
frameworks/JavaScript/koa/handlers/handler.js

@@ -0,0 +1,49 @@
+const h = require('../helper');
+const Bluebird = require('bluebird');
+
+/**
+ * @param databaseLayer
+ * @returns {{SingleQuery: function(*), MultipleQueries: function(*), Fortunes: function(*), Updates: function(*)}}
+ */
+module.exports = (databaseLayer) => ({
+  SingleQuery: async (ctx) => {
+    ctx.body = await databaseLayer.getWorldLean(h.randomTfbNumber());
+  },
+
+  MultipleQueries: async (ctx) => {
+    const queries = h.getQueries(ctx.request.query.queries);
+    const promisesArray = [];
+    for (let i = 0; i < queries; i++) {
+      promisesArray.push(databaseLayer.getWorldLean(h.randomTfbNumber()));
+    }
+    ctx.body = await Bluebird.all(promisesArray)
+  },
+
+  Fortunes: async (ctx) => {
+    const fortunes = await databaseLayer.allFortunes();
+    fortunes.push(h.additionalFortune);
+    fortunes.sort((a, b) => a.message.localeCompare(b.message));
+
+    return ctx.render('fortunes', {fortunes});
+  },
+
+  Updates: (ctx) => {
+    const queries = h.getQueries(ctx.request.query.queries);
+    const worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(databaseLayer.getWorld(h.randomTfbNumber()));
+    }
+
+    return Bluebird
+      .all(worldPromises)
+      .map(world => {
+        world.randomNumber = h.randomTfbNumber();
+        return world
+      })
+      .then(worlds => databaseLayer.saveWorlds(worlds))
+      .then(worlds => {
+        ctx.body = worlds;
+      });
+  }
+});

+ 7 - 108
frameworks/JavaScript/koa/handlers/mongoose.js

@@ -1,110 +1,9 @@
-// Connects to MongoDB using the mongoose driver
-// Handles related routes
-
-const Promise = require('bluebird');
-const h = require('../helper');
-// Can treat mongoose library as one that supports Promises
-// these methods will then have "-Async" appended to them.
-const Mongoose = Promise.promisifyAll(require('mongoose'));
-const connection = Mongoose.connect(
-  'mongodb://TFB-database/hello_world',
-  { useMongoClient: true }
-);
-
-const WorldSchema = new Mongoose.Schema({
-    id :          Number,
-    randomNumber: Number
-  }, {
-    collection: 'world'
-  });
-const FortuneSchema = new Mongoose.Schema({
-    id:      Number,
-    message: String
-  }, {
-    collection: 'fortune'
-  });
-
-const Worlds = connection.model('World', WorldSchema);
-const Fortunes = connection.model('Fortune', FortuneSchema);
-
-const randomWorldPromise = () =>
-  Worlds.findOneAsync({ id: h.randomTfbNumber() })
-    .then((world) => world)
-    .catch((err) => process.exit(1));
-
-
-const promiseAllFortunes = () =>
-  Fortunes.findAsync({})
-    .then((fortunes) => fortunes)
-    .catch((err) => process.exit(1));
-
-const updateWorld = (world) =>
-  Worlds
-    .updateAsync(
-      { id: world.randomNumber },
-      { randomNumber: world.randomNumber }
-    )
-    .then((result) => world)
-    .catch((err) => process.exit(1));
+const {Worlds, Fortunes} = require('../models/mongoose');
+const Bluebird = require('bluebird');
 
 module.exports = {
-
-  SingleQuery: (ctx, next) => {
-    return randomWorldPromise()
-      .then((world) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = world;
-        return next();
-      });
-  },
-
-  MultipleQueries: (ctx, next) => {
-    const queries = h.getQueries(ctx);
-    const worldPromises = h.fillArray(randomWorldPromise(), queries);
-
-    return Promise
-      .all(worldPromises)
-      .then((worlds) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = worlds;
-        return next()
-      });
-  },
-
-  Fortunes: (ctx, next) => {
-    return promiseAllFortunes()
-      .then((fortunes) => {
-        fortunes.push(h.additionalFortune());
-        fortunes.sort((a, b) => a.message.localeCompare(b.message));
-      
-        ctx.type = 'text/html';
-        ctx.set('Server', 'Koa');
-        return ctx.render('fortunes', { fortunes: fortunes });
-      });
-  },
-
-  Updates: (ctx, next) => {
-    const queries = h.getQueries(ctx);
-    const worldPromises = [];
-
-    for (let i = 0; i < queries; i++) {
-      worldPromises.push(randomWorldPromise());
-    }
-
-    return Promise
-      .all(worldPromises)
-      .map((world) => {
-        world.randomNumber = h.randomTfbNumber();
-        return updateWorld(world);
-      })
-      .then((worlds) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = worlds;
-        return next();
-      });
-  }
-
-};
+  getWorld: _id => Worlds.findById(_id),
+  getWorldLean: _id => Worlds.findById(_id).lean(),
+  allFortunes: () => Fortunes.find({}),
+  saveWorlds: worlds => Bluebird.all(worlds.map(world => world.save())),
+};

+ 7 - 107
frameworks/JavaScript/koa/handlers/sequelize-postgres.js

@@ -1,109 +1,9 @@
-// Connects to Postgres using the sequelize driver
-// Handles related routes
-const Promise = require('bluebird');
-const h = require('../helper');
-const Sequelize = require('sequelize');
-
-const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
-  host: 'TFB-database',
-  dialect: 'postgres',
-  logging: false
-});
-
-const Worlds = sequelize.define('world', {
-  id: {
-    type: 'Sequelize.INTEGER',
-    primaryKey: true
-  },
-  randomNumber: { type: 'Sequelize.INTEGER', field: 'randomnumber' }
-}, {
-  timestamps: false,
-  freezeTableName: true
-});
-
-const Fortunes = sequelize.define('fortune', {
-  id: {
-    type: 'Sequelize.INTEGER',
-    primaryKey: true
-  },
-  message:      { type: 'Sequelize.STRING' }
-}, {
-  timestamps: false,
-  freezeTableName: true
-});
-
-const randomWorldPromise = () =>
-  Worlds.findOne({ where: { id: h.randomTfbNumber() } })
-    .then((results) => results)
-    .catch((err) => process.exit(1));
+const {Worlds, Fortunes} = require('../models/sequelize-postgres');
+const Bluebird = require('bluebird');
 
 module.exports = {
-
-  SingleQuery: (ctx, next) =>
-    randomWorldPromise().then((world) => {
-      ctx.set('Server', 'Koa');
-      ctx.type = 'application/json';
-      ctx.body = world;
-      return next();
-    }),
-
-  MultipleQueries: (ctx, next) => {
-    const queries = h.getQueries(ctx),
-      worldPromises = [];
-
-    for (let i = 0; i < queries; i++) {
-      worldPromises.push(randomWorldPromise());
-    }
-
-    return Promise.all(worldPromises)
-      .then((worlds) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = worlds;
-        return next();
-      });
-  },
-
-  Fortunes: (ctx, next) => {
-    return Fortunes.findAll().then((fortunes) => {
-      fortunes.push(h.additionalFortune());
-      fortunes.sort((a, b) => a.message.localeCompare(b.message));
-
-      ctx.set('Server', 'Koa');
-      ctx.type = 'text/html';
-      return ctx.render('fortunes', { fortunes });
-    }).catch((err) => process.exit(1));
-  },
-
-  Updates: (ctx, next) => {
-    const queries = h.getQueries(ctx),
-      worldPromises = [];
-
-    for (let i = 0; i < queries; i++) {
-      worldPromises.push(randomWorldPromise());
-    }
-
-    const worldUpdate = (world) => {
-      world.randomNumber = h.randomTfbNumber();
-
-      return Worlds.update(
-        {randomNumber: world.randomNumber},
-        {where: {id: world.id}}
-      )
-        .then((results) => world)
-        .catch((err) => process.exit(1));
-    };
-
-    return Promise
-      .all(worldPromises)
-      .map((world) => worldUpdate(world))
-      .then((updated) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = updated;
-        return next();
-      })
-      .catch((err) => process.exit(1));
-  }
-
-};
+  getWorld: id => Worlds.findOne({where: {id}}),
+  getWorldLean: id => Worlds.findOne({where: {id}, raw: true}),
+  allFortunes: () => Fortunes.findAll(),
+  saveWorlds: worlds => Bluebird.all(worlds.map(world => world.save())),
+};

+ 7 - 107
frameworks/JavaScript/koa/handlers/sequelize.js

@@ -1,109 +1,9 @@
-// Connects to MySQL using the sequelize driver
-// Handles related routes
-const Promise = require('bluebird');
-const h = require('../helper');
-
-const Sequelize = require('sequelize');
-const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
-  host: 'TFB-database',
-  dialect: 'mysql',
-  logging: false
-});
-
-const Worlds = sequelize.define('world', {
-  id: {
-    type: 'Sequelize.INTEGER',
-    primaryKey: true
-  },
-  randomNumber: { type: 'Sequelize.INTEGER' }
-}, {
-  timestamps: false,
-  freezeTableName: true
-});
-
-const Fortunes = sequelize.define('Fortune', {
-  id: {
-    type: 'Sequelize.INTEGER',
-    primaryKey: true
-  },
-  message: { type: 'Sequelize.STRING' }
-}, {
-  timestamps: false,
-  freezeTableName: true
-});
-
-const randomWorldPromise = () =>
-  Worlds.findOne({ where: { id: h.randomTfbNumber() } })
-    .then((results) => results)
-    .catch((err) => process.exit(1));
-
+const {Worlds, Fortunes} = require('../models/sequelize');
+const Bluebird = require('bluebird');
 
 module.exports = {
-
-  SingleQuery: (ctx, next) => {
-    return randomWorldPromise().then((world) => {
-      ctx.set('Server', 'Koa');
-      ctx.type = 'application/json';
-      ctx.body = world;
-      return next();
-    })
-  },
-
-  MultipleQueries: (ctx, next) => {
-    const queries = h.getQueries(ctx);
-    const worldPromises = [];
-
-    for (let i = 0; i < queries; i++) {
-      worldPromises.push(randomWorldPromise());
-    }
-
-    return Promise.all(worldPromises).then((worlds) => {
-      ctx.set('Server', 'Koa');
-      ctx.type = 'application/json';
-      ctx.body = worlds;
-      return next();
-    });
-  },
-
-  Fortunes: (ctx, next) => {
-    return Fortunes.findAll().then((fortunes) => {
-      fortunes.push(h.additionalFortune());
-      fortunes.sort((a, b) => a.message.localeCompare(b.message));
-      
-      ctx.set('Server', 'Koa');
-      ctx.type = 'text/html';
-      return ctx.render('fortunes', { fortunes });
-    }).catch((err) => process.exit(1));
-  },
-
-  Updates: (ctx, next) => {
-    const queries = h.getQueries(ctx);
-    const worldPromises = [];
-
-    for (let i = 0; i < queries; i++) {
-      worldPromises.push(randomWorldPromise());
-    }
-
-    const worldUpdate = (world) => {
-      world.randomNumber = h.randomTfbNumber();
-
-      return Worlds.update(
-          { randomNumber: world.randomNumber },
-          { where: { id: world.id } }
-        )
-        .then((results) => world)
-        .catch((err) => process.exit(1));
-    };
-
-    return Promise
-      .all(worldPromises)
-      .map((world) => worldUpdate(world))
-      .then((updated) => {
-        ctx.set('Server', 'Koa');
-        ctx.type = 'application/json';
-        ctx.body = updated;
-        return next();
-      })
-      .catch((e) => process.exit(1));
-  }
-};
+  getWorld: id => Worlds.findOne({where: {id}}),
+  getWorldLean: id => Worlds.findOne({where: {id}, raw: true}),
+  allFortunes: () => Fortunes.findAll(),
+  saveWorlds: worlds => Bluebird.all(worlds.map(world => world.save())),
+};

+ 4 - 21
frameworks/JavaScript/koa/helper.js

@@ -1,29 +1,12 @@
-const Handlebars = require('handlebars');
-
-const GREETING = "Hello, World";
-const HELLO_OBJ = { message: GREETING };
-
 module.exports = {
   randomTfbNumber: () => Math.floor(Math.random() * 10000) + 1,
 
-  fillArray: (value, len) => {
-    const filled = [];
-
-    for (let i = 0; i < len; i++) {
-      filled.push(value);
-    }
-    return filled;
+  getQueries: (queries) => {
+    return Math.min(Math.max(parseInt(queries) || 1, 1), 500);
   },
 
-  getQueries: (ctx) => {
-    let queries = ~~(ctx.query.queries) || 1;
-    queries = Math.min(Math.max(queries, 1), 500);
-    return queries;
-  },
-
-  additionalFortune: () => ({
+  additionalFortune: {
     id: 0,
     message: 'Additional fortune added at request time.'
-  })
-
+  }
 };

+ 31 - 0
frameworks/JavaScript/koa/models/mongoose.js

@@ -0,0 +1,31 @@
+const Bluebird = require('bluebird');
+const Mongoose = require('mongoose');
+
+Mongoose.Promise = Bluebird;
+
+const {SchemaTypes} = Mongoose;
+
+Mongoose.connect('mongodb://TFB-database/hello_world')
+  .catch(error => {
+    console.error(error);
+    process.exit(1);
+  });
+
+const WorldSchema = new Mongoose.Schema({
+  _id: SchemaTypes.Number,
+  randomNumber: SchemaTypes.Number,
+}, {
+  collection: 'world',
+  versionKey: false,
+});
+
+const FortuneSchema = new Mongoose.Schema({
+  id: SchemaTypes.Number,
+  message: SchemaTypes.String,
+}, {
+  collection: 'fortune',
+  versionKey: false,
+});
+
+module.exports.Worlds = Mongoose.model('World', WorldSchema, 'world');
+module.exports.Fortunes = Mongoose.model('Fortune', FortuneSchema, 'fortune');

+ 37 - 0
frameworks/JavaScript/koa/models/sequelize-postgres.js

@@ -0,0 +1,37 @@
+const Sequelize = require('sequelize');
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'TFB-database',
+  dialect: 'postgres',
+  logging: false
+});
+
+const {DataTypes} = Sequelize;
+const Worlds = sequelize.define('World', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  randomNumber: {
+    type: DataTypes.INTEGER,
+    field: 'randomnumber'
+  },
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  message: {type: DataTypes.STRING}
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+module.exports = {
+  Worlds,
+  Fortunes,
+};

+ 34 - 0
frameworks/JavaScript/koa/models/sequelize.js

@@ -0,0 +1,34 @@
+const Sequelize = require('sequelize');
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'TFB-database',
+  dialect: 'mysql',
+  logging: false
+});
+
+const {DataTypes} = Sequelize;
+const Worlds = sequelize.define('World', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  randomNumber: {type: DataTypes.INTEGER}
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  message: {type: DataTypes.STRING}
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+module.exports = {
+  Worlds,
+  Fortunes,
+};

+ 12 - 12
frameworks/JavaScript/koa/package.json

@@ -1,20 +1,20 @@
 {
   "name": "koa-tfb",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "Koa tests for TechEmpower Framework Benchmarks.",
   "main": "app.js",
   "private": true,
   "dependencies": {
-    "bluebird": "3.5.0",
-    "handlebars": "4.0.8",
-    "koa": "2.2.0",
-    "koa-bodyparser": "4.2.0",
-    "koa-hbs": "1.0.0-alpha.1",
-    "koa-router": "7.1.1",
-    "mongoose": "4.12.4",
-    "mysql": "2.13.0",
-    "pg": "6.2.2",
-    "pg-hstore": "2.3.2",
-    "sequelize": "3.30.4"
+    "bluebird": "~3.5.1",
+    "handlebars": "~4.0.11",
+    "koa": "~2.5.0",
+    "koa-bodyparser": "~4.2.0",
+    "koa-hbs": "~1.0.0",
+    "koa-router": "~7.4.0",
+    "mongoose": "~5.0.11",
+    "mysql2": "~1.5.3",
+    "pg": "~7.4.1",
+    "pg-hstore": "~2.3.2",
+    "sequelize": "~4.37.3"
   }
 }