I love generators in nodejs. They help nodejs look more like server-side code. I'm trying to use generators with my Sails app. This is my controller and it works when I visit 'get /hi':
/**
* FooController
*
* #description :: Server-side logic for managing foos
* #help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
hi: function (req, res){
return res.send("Hi there!");
}
};
However when I change that hi action to a generator function...
module.exports = {
hi: function* (req, res){
return res.send("Hi there!");
}
};
this action never gets to return the response. Like it's waiting for something. How does one utilize ES6 generators within SailsJS controllers and in-fact all of Sails?
You can use it, in fact it'll be great if we all use this style, it adds a lot of readability and flexibility in your code (no more callback-hell), but that is not the way to use it.
As #Cohars stated, sails expect a function as controller actions, you can't pass a generator like in Koa, but that does not prevent you from using them, the thing is that a generator by itself is very useless, you need a function that calls it and iterates it and i believe koa does this for you at framework level, but you have some options available to you at library level, like co or yortus/asyncawait/ which is what i use because node-fibers implemented as functions are way more flexible than es6 generators (though they are almost the same) and i'll show you why in a sec.
So here is how you use it:
First npm install co and require it in your controller, or add it as a global in config/bootstrap.js since you will be using it a lot. Once you are done with it, you can use it in your controllers.
module.exports = {
hi: function(req, res){
co(function* (){
// And you can use it with your models calls like this
let user = yield User.findOne(req.param('id'))
return res.send("Hi there" + user.name + "!");
})
}
};
That's it
Now if you rather use async/await, its pretty similar, it goes like this:
module.exports = {
hi: function(req, res){
async(function(){
// Since await is a function, you can access properties
// of the returned values like this which is nice to
// access object properties or array indices
let userName = await(User.findOne(req.param('id'))).name
return res.send("Hi there" + userName + "!");
})();
}
};
There is a caveat though, if you call other methods of you controller via this, remember that they will refer to the generator fn or the passed regular fn if you use async/await, so be sure to save its context, or since you are already using es6 syntax, you can use fat arrows with async/await, unfortunately not with co, since there is not fat arrow version of generators (..yet?)
So it'll be like this:
module.exports = {
hi: function(req, res){
async(() => {
let params = this._parseParams(req);
let userName = await(User.findOne(params.id)).name
return res.send("Hi there" + userName + "!");
})();
},
_parseParams: function(req){
let params = req.allParams()
// Do some parsing here...
return params
}
};
I've been using the second method for months now, and works perfectly, i've tried with co too and works as well, i just liked more the async/await module (and is supposed to be a little bit faster) and its a perfect match if you are using coffeescript since your sintax will be like do async => await User.find()
UPDATE:
I've created a wrapper that you can use if you use yortus async/await or check the source code and modify it to work with co or something else if you wish.
Here is the link https://www.npmjs.com/package/async-handler, is in alpha but i'm using on my own apps and works as expected, if not submit an issue.
With that the las example would look like this:
module.exports = {
hi: asyncHandler((req, res)->{
let params = this._parseParams(req);
let userName = await(User.findOne(params.id)).name
return res.send("Hi there" + userName + "!");
}),
_parseParams: function(req){
let params = req.allParams()
// Do some parsing here...
return params
}
};
With this handler you get the extra benefit of having async/promise errors to propagate correctly on sails if they are not caught by a try/catch
Sails expects a regular function here, not a generator. Maybe you could take a look at co, not sure it would really help with Sails though. If you really want to use generators, you should probably try Koa, which has several frameworks based on it
The way I am doing it is like this:
const Promise = require('bluebird');
module.exports = {
hi: Promise.coroutine(function* (req, res) {
let id = req.params('id'),
userName;
try {
userName = yield User.findOne(id).name;
}
catch (e) {
sails.log.error(e);
return res.serverError(e.message);
}
return res.ok(`Hi there ${userName}!`);
})
}
Works great. You just need to ensure any functions you call from your controllers return promises.
Put this code in your bootstrap.js, and every thing work like a charm!
var _ = require('lodash');
var coExpress = require('co-express');
sails.modules.loadControllers(function (err, modules) {
if (err) {
return callback(err);
}
sails.controllers = _.merge(sails.controllers, modules);
// hacking every action of all controllers
_.each(sails.controllers, function(controller, controllerId) {
_.each(controller, function(action, actionId) {
actionId = actionId.toLowerCase();
console.log('hacking route:', controllerId, actionId);
// co.wrap,generator => callback
action = coExpress(action);
sails.hooks.controllers.middleware[controllerId][actionId] = action;
});
});
// reload routes
sails.router.load(function () {
// reload blueprints
sails.hooks.blueprints.initialize(function () {
sails.hooks.blueprints.extendControllerMiddleware();
sails.hooks.blueprints.bindShadowRoutes();
callback();
});
});
});
Related
I want to force Node to wait for the promise to complete (either by failure or success). So far, I'm not seeing what I wanted. I'm trying to wait for two web services to complete before merging, and especially before the function ends.
I've tried these two approaches below. In both cases, Node refuses to wait.
Approach 1:
function getStrategy() {
// this is a web service that takes a few ms to run,
// but so far I haven't seen any evidence that it bothers.
}
function getConfig() {
// Both strategy and jwt are set to Promises
const strategy = getStrategy();
const jwt = getJwt();
const lodash = require('lodash');
var config = {};
try {
Promise.all([strategy,jwt])
.then(data => { config = lodash.merge(config,data)})
} catch(error) {
console.log(' Print error message');
}
return config;
}
Approach 2:
function getStrategy() {
// this is a web service that takes a few ms to run,
// but so far I haven't seen any evidence that it bothers.
}
async function getConfig() {
// Both strategy and jwt are set to Promises
const strategy = getStrategy();
const jwt = getJwt();
const lodash = require('lodash');
var config = {};
try {
var promiseResult = await Promise.all([strategy, jwt]);
const lodash = require('lodash');
config = lodash.merge(config, strategy[0]);
config = lodash.merge(config, jwt);
} catch (reason) {
console.error('------------------------------------------------------------------------------------');
console.error(reason);
console.error("in getConfig(): Could not fetch strategy or jwt");
console.error('------------------------------------------------------------------------------------');
}
return config;
}
I wish approach 2 worked, but it does not. It will not print any console.log statements after the call to Promise.all. So within that function it does wait. Except that it because I told it to "await", I have to make the function async. That allows Node to say, "oh, it's an async function, I can just go off and do something else and completely ignore the await keyword." It does this by returning to the function calling getConfig().
In the first approach, neither the "then" handler, nor any exceptions are thrown. It just impatiently leaves the function and goes back to the caller.
How do I get the thread that calls the getConfig() function to wait for the result. I mean really wait, not like, partially "await". Or, throw an exception and let me handle that. I'm finding that in Node, as soon as something because asynchronous, I have no idea how to get the await, or the then handler to work.
Updated attempt:
I separated the two service calls to individually control each service. I now have
async function getSynchronousStrategy(isVaultAvailable) {
const secretsVaultReader = require('./src/configuration/secretsVaultReader');
const secretsConfigReader = require('./src/configuration/secretsConfigReader');
const strategy = isVaultAvailable
? secretsVaultReader.getStrategy()
: secretsConfigReader.getStrategy();
await strategy;
console.log('## strategy after await=' + strategy);
return strategy; ''
}
...
const strategy = await getSynchronousStrategy(isVaultAvailable);
console.log('### strategy =' + JSON.stringify(strategy));
...
In the above case, Node sees the await strategy, but then prints
"## strategy after await=[object Promise]"
However, the second await seems to work, and it prints the desired strategy. I'm guessing the promise was eventually settled and it was able to print the result. I don't mind the time, I just want it to wait.
Obviously, it did not wait. It
I would suggest simply using await with both the function calls directly, in this manner it will wait for both the functions to return responses before proceeding further.
I am creating a simple CRUD Board through Express.
I implemented the CRU, but the delete function failed. I used Rails method='delete' as a common anchor tag, but Express does not seem to support it.
How can I activate the delete link?
app.js
...
const board = require("./routes/board");
app.use("/board", board);
...
views
a(href=`/board/${board._id} method="delete"`) 삭제
routes
...
const board = require("../logic/board");
router.delete("/:id", board.delete);
...
logic
...
const Board = require("../db/board");
exports.delete = (req, res) =>{
Board.findByIdAndRemove(req.params.id, err => {
if (err) {
return next(err);
}
res.redirect("/board/index");
});
}
...
And I want to ask. What is the difference in behavior between doing something like exports.delete = () => {} and doing something like module.exports = logic <delete, create etc...>?
HTML <a>nchor tags don't have a method attribute -- perhaps you're thinking of <form> tags?
What is the difference in behavior between doing something like exports.delete = () => {} and doing something like module.exports = logic
The difference is when your logic gets executed. The exports.delete = () => {} form will export a function that you can execute at some point in the future, whereas the module.exports = Board.findByIdAndRemove() will execute the database query immediately when the file is parsed (which you probably don't want).
In the docs for Application there's a note on using context to expose widely used properties in ctx:
For example, to add a reference to your database from ctx:
app.context.db = db();
app.use(async ctx => {
console.log(ctx.db);
});
However, it also notes that relying more on ctx "could be considered an anti-pattern". Fair enough.
Then we have koa-knex-realworld-example which suggests something like this:
module.exports = function (app) {
// ...
const db = require('knex')(config.db)
app.db = db
// ...
return async function (ctx, next) {
if (ctx.app.migration && promise) {
await promise
}
return next()
}
}
And passes the app instance to the middleware:
app.use(db(app))
That feels strange to me. Having to pass app to its own middleware seems a little backwards, but maybe it's a known Koa pattern?
Finally, we have koa-knex which exposes knex as this.knex in middleware further down the stack:
app.use(_.get('/:userid', function *(userid) {
this.body = {
profile: yield this.knex('users').where('id', userid);
};
});
That one's a bit dated (generators, which are fine but not the default API for Koa anymore) and chucks Knex in global:
global.__knex || (global.__knex = Knex.initialize({
// ...
}));
this.knex = global.__knex;
yield next;
What's the alternative?
In summary, I think I'm pretty comfortable putting a reference to Knex in the context, and I don't think I'll be tempted to pollute it with too much else. However, if I wanted an alternative, is there a middleware approach to making Knex available in Koa2 that is preferable to the above options?
Is it possible to process a db.model.find() query inside of function context and retrieve a result without using callbacks and promises with mongoose library?
I need to get assured, if some user exists in process of running controller, so, I can't minimize current scope to callback due to large amount of same operations (for example, communication with database). Also I'm trying to realize MVC model in my project, so, I want to keep the helper libs (modules) in separated files. That's why I don't want to use any callbacks or promises - they will much times complicate everything even more then things already do.
For example, how should I rewrite the following code to be executed successfully (if it's actually possible) (you can ignore login model and controller - they are written to represent complicacy if to rewrite that code using callbacks):
user.js lib
var db = require('./lib/db');
class User{
constructor(id){ //get user by id
var result = db.models.user.findOne({_id: id}); //unsupported syntax in real :(
if(!result || result._id != _id)
return false;
else{
this.userInfo = result;
return result;
}
}
}
module.exports = User;
login model
var user = require('./lib/user')
var model = {};
model.checkUserLogged(function(req){
if(!req.user.id || req.user.id == undefined)
return false;
if(!(this.user = new user(req.user.id)))
return false;
else
return true;
});
module.exports = model;
login controller
var proxy = require('express').router();
proxy.all('/login', function(req, res){
var model = require('./models/login');
if(!model.checkUserLogged()){
console.log('User is not logged in!');
res.render('unlogged', model);
}else{
console.log('User exists in database!');
res.render('logged_in', model);
}
});
Generator functions/yields, async/await (es2017), and everything et cetera can be used just to solve the problem without nesting.
Thx in advance.
There are two points wrong:
Mongoose methods can't be called synchronously (Anyway a call to a DB done synchronously is not a good idea at all).
Nor async/await nor generators can be used in the constructor of an ES6 Class. It is explained in this answer.
If you don't want nested code an easy option could be to use async/await (currently available in Node.js using a flag, not recommended for production). Since Mongoose methods return promises they can be used with async/await.
But as I said you can not do that in the constructor, so it has to be somewhere else.
As an example you could do something like this:
var proxy = require('express').router();
var db = require('./lib/db');
proxy.all('/login', async function(req, res){
const result = await db.models.user.findOne({_id: req.user.id}).exec();
if (!result) {
console.log('User is not logged in!');
return res.render('unlogged');
}
res.render('logged_in');
});
Old question, but I want to share a method for handling this that I didn't see in my first couple searches.
I want to get data from a model, run some logic and return the results from that logic. I need a promise wrapper around my call to the model.
Below is a slightly abstracted function that takes a model to run a mongoose/mongo query on, and a couple params to help it do some logic. It then returns the value that is expected in the promise or rejects.
export function promiseFunction(aField: string, aValue, model: Model<ADocument, {}>): Promise<aType> {
return new Promise<string>((resolve, reject) => {
model.findOne({[aField]: aValue}, (err, theDocument) => {
if(err){
reject(err.toString());
} else {
if(theDocument.someCheck === true){
return(theDocument.matchingTypeField)
} else {
reject("there was an error of some type")
}
}
});
})
}
I'm trying to avoid using callbacks when making mongodb queries. I'm using mongoskin to make calls like so:
req.db.collection('users').find().toArray(function (err, doc) {
res.json(doc);
});
In many cases I need to make multiple queries so I want to use Node.js promise library but I'm not sure how to wrap these functions as promises. Most of the examples I see are trivial for things like readFile, I'm guessing in this case I would need to wrap toArray somehow? Can this be done or would have to be something implemented by mongoskin?
An example could be any set of callbacks, find/insert, find/find/insert, find/update:
req.db.collection('users').find().toArray(function (err, doc) {
if (doc) {
req.db.collection('users').find().toArray(function (err, doc) {
// etc...
});
}
else {
// err
}
});
You can promisify the entire module like so with bluebird:
var Promise = require("bluebird");
var mongoskin = require("mongoskin");
Object.keys(mongoskin).forEach(function(key) {
var value = mongoskin[key];
if (typeof value === "function") {
Promise.promisifyAll(value);
Promise.promisifyAll(value.prototype);
}
});
Promise.promisifyAll(mongoskin);
This only needs to be done in one place for one time in your application, not anywhere in your application code.
After that you just use methods normally except with the Async suffix and don't pass callbacks:
req.db.collection('users').find().toArrayAsync()
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
})
.then(function(doc) {
if (doc) {
return req.db.collection('users').find().toArrayAsync();
}
});
So again, if you call a function like
foo(a, b, c, function(err, result) {
if (err) return console.log(err);
//Code
});
The promise-returning version is called like:
fooAsync(a, b, c).then(...)
(Uncaught errors are automatically logged so you don't need to check for them if you are only going to log it)
Just stumbled here with the same question and didn't love "promisfying" mongoskin so did a bit more digging and found monk. It's built on top of mongoskin, tidies up the API and returns
promises for all async calls. Probably worth a peek to anyone else who lands here.
Esailija's answer may work, but its not super efficient since you have to run db.collection on every single db call. I don't know exactly how expensive that is, but looking at the code in mongoskin, its non-trivial. Not only that, but it's globally modifying prototypes, which isn't very safe.
The way I do this with fibers futures is:
wrap the collection methods for each collection
on receiving the result, for methods that return a Cursor wrap the toArray method, call it and return the resulting future (for methods that don't return a cursor, you don't need to do anything else).
use the future as normal
like this:
var Future = require("fibers/future")
// note: when i originally wrote this answer fibers/futures didn't have a good/intuitive wrapping function; but as of 2014-08-18, it does have one
function futureWrap() {
// function
if(arguments.length === 1) {
var fn = arguments[0]
var object = undefined
// object, methodName
} else {
var object = arguments[0]
var fn = object[arguments[1]]
}
return function() {
var args = Array.prototype.slice.call(arguments)
var future = new Future
args.push(future.resolver())
var me = this
if(object) me = object
fn.apply(me, args)
return future
}
}
var methodsYouWantToHave = ['findOne', 'find', 'update', 'insert', 'remove', 'findAndModify']
var methods = {}
methodsYouWantToHave.forEach(function(method) {
internalMethods[method] = futureWrap(this.collection, method)
}.bind(this))
// use them
var document = methods.findOne({_id: 'a3jf938fj98j'}, {}).wait()
var documents = futureWrap(methods.find({x: 'whatever'}, {}).wait(), 'toArray')().wait()
If you don't want to use fibers, I'd recommend using the async-future module, which has a good wrap function built in too.