Loopback JS model connetion with MongoDB - node.js

I want to use LoopbackJS framework to write some quick APIs.
The thing is that I want to connect my model with the mongodb loopback connector to do some simple find queries.
These are my files:
/server/model-config.js
"transaction": {
"dataSource": "mongo",
"public": true
}
/server/datasources.json
"mongo": {
"name": "mongo",
"connector": "mongodb"
}
/common/models/transaction.js
module.exports = function(Transaction) {
Transaction.find({}, function(err, data) {
console.log(data);
});
};
The following is failing with this error:
Error: Cannot call transaction.find(). The find method has not been setup. The PersistedModel has not been correctly attached to a DataSource!
What am I doing wrong?

At the moment that this code is executed
Transaction.find({}, function(err, data) {
console.log(data);
});
The framework is not prepared to do things yet. You should register a hook callback in order to execute your business logic.
A example would be:
Transaction.observe('before save', function doStuf(ctx, next) {
Transaction.find({}, function(err, data) {
console.log(data);
next();// be sure to call the callback function
});
}
Other hooks are defined in this link:
https://docs.strongloop.com/display/public/LB/Adding+logic+to+models

Related

node-soap service that uses database (dependency issues)

First of all, this is one of my first projects in Node.js so I'm very new to it.
I have a project I want to make that is a SOAP (I know, SOAP... backwards compatibility, huh?) interface that connects to an Oracle database.
So I have a WSDL describing what these functions look like (validation for addresses and stuff) and I have a connection to the database.
Now when using the SOAP npm module, you need to create a server and listen using a service that allows you to respond to requests. I have a separate file that contains my SOAP service but this service should do queries on the database to get its results.
How would I go about sort of 'injecting' my database service into my SOAP service so that whenever a SOAP call is done, it orchestrates this to the correct method in my database service?
This is what my code looks like:
databaseconnection.js
var oracledb = require('oracledb');
var dbConfig = require('../../config/development');
var setup = exports.setup = (callback) => {
oracledb.createPool (
{
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
},
function(err, pool)
{
if (err) { console.error(err.message); return; }
pool.getConnection (
function(err, connection)
{
if (err) {
console.error(err.message);
return callback(null);
}
return callback(connection);
}
);
}
);
};
databaseservice.js
var DatabaseService = function (connection) {
this.database = connection;
};
function doSomething(callback) {
if (!this.database) { console.log('Database not available.'); return; }
this.database.execute('SELECT * FROM HELP', function(err, result) {
callback(result);
});
};
module.exports = {
DatabaseService: DatabaseService,
doSomething: doSomething
};
soapservice.js
var myService = {
CVService: {
CVServicePort: {
countryvalidation: function (args, cb, soapHeader) {
console.log('Validating Country');
cb({
name: args
});
}
}
}
};
server.js
app.use(bodyParser.raw({type: function(){return true;}, limit: '5mb'}));
app.listen(8001, function(){
databaseconnection.setup((callback) => {
var temp = databaseservice.DatabaseService(callback);
soapservice.Init(temp);
var server = soap.listen(app, '/soapapi/*', soapservice.myService, xml);
databaseservice.doSomething((result) => {
console.log(result.rows.length, ' results.');
});
});
console.log('Server started');
});
How would I go about adding the databaseservice.doSomething() to the countryvalidation soap method instead of 'name: args'?
Also: I feel like the structure of my code is very, very messy. I tried finding some good examples on how to structure the code online but as for services and database connections + combining them, I didn't find much. Any comments on this structure are very welcome. I'm here to learn, after all.
Thank you
Dieter
The first thing I see that looks a little off is the databaseconnection.js. It should be creating the pool, but that's it. Generally speaking, a connection should be obtained from the pool when a request comes in and release when you're done using it to service that request.
Have a look at this post: https://jsao.io/2015/02/real-time-data-with-node-js-socket-io-and-oracle-database/ There are some sample apps you could have a look at that might help. Between the two demos, the "employees-cqn-demo" app is better organized.
Keep in mind that the post is a little dated now, we've made enhancements to the driver that make it easier to use now. It's on my list to do a post on how to build a RESTful API with Node.js and Oracle Database but I haven't had a chance to do it yet.

Mongoose: Model.remove callback arguments differ

I'm using the async waterfall method in my application and I need to specify the arguments in every callback function.
async.waterfall([
function (next) {
Client.remove({ store: store }, next);
},
function (objectsRemoved, next) {
Item.remove({ store: store }, next);
},
function (objectsRemoved, next) {
Store.remove({ _id: store.id }, next);
},
// ...
The code above works fine in the server, but fails in my local environment. This is because the callback arguments for mongoose's Model.remove differ on both sides.
In the server I get function (err, objectsRemoved)
Locally I get function (err, objectsRemoved, anotherObject)
The weird thing is that in both cases I'm running the same version of mongodb (2.4.9) and mongoose (3.8.20).
Any idea of what could be happening?

How to connect CouchDb with HapiJs?

I have an App written using the HapiJs framework for Node and want to connect it to a CouchDb databse, but am having trouble finding the code to do so.
Can anyone help me with the code to do that? What is the 'normal' way of doing that?
Cheers!
Well you don't need any framework for couchdb. Everything is available via a rest api. Just use request module to make requests to the api. A few examples : -
Read a document
request.get("http://localhost:5984/name_of_db/id_of_docuement",
function(err,res,data){
if(err) console.log(err);
console.log(data);
});
Read from a view
request.get(
"http://localhost:5984/name_of_db/_design/d_name/_view/_view_name",
function(err,res,data){
if(err) console.log(err);
console.log(data);
});
The entire api is documented here
There is no need to manage connections or to handle the opening and closing of database that you might be doing with other databases. Simply start couchdb and start making requests to from your application.
However if you find that making requests to the api directly is a bit cumbersome for you, then you can try using nano which provides a nicer syntax for doing things with couchdb.
Some snippets of code
All right so I am not familliar with hapi so I will just tell you how do do it with request.
Consider this example from the docs
var Hapi = require('hapi');
var server = new Hapi.Server(3000);
var request = require("request");
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello, world!');
}
});
server.route({
method: 'GET',
path: '/{name}',
handler: function (req, rep) {
request.get("http://localhost:5984/name_of_db/id_of_docuement",
function(err,res,data){
if(err) console.log(err);
rep(data);
});
}
});
server.start(function () {
console.log('Server running at:', server.info.uri);
});
When you call the / endpoint it the request handler for it is executed. It makes a request to a couchdb endpoint to fetch a a document. You don't need anything to connect to couchdb besides that.
Another option could be the hapi-couchdb plugin (https://github.com/harrybarnard/hapi-couchdb).
Using it is a little more "hapi-like" than making direct calls into the Couch API directly.
Here's an example from the plugin documentation:
var Hapi = require('hapi'),
server = new Hapi.Server();
server.connection({
host: '0.0.0.0',
port: 8080
});
// Register plugin with some options
server.register({
plugin: require('hapi-couchdb'),
options: {
url: 'http://username:password#localhost:5984',
db: 'mycouchdb'
}
}, function (err) {
if(err) {
console.log('Error registering hapi-couchdb', err);
} else {
console.log('hapi-couchdb registered');
}
});
// Example of accessing CouchDb within a route handler
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
var CouchDb = request.server.plugins['hapi-couchdb'];
// Get a document from the db
CouchDb.Db.get('rabbit', { revs_info: true }, function(err, body) {
if (err) {
throw new Error(CouchDb.Error(error); // Using error decoration convenience method
} else {
reply(body);
});
}
});
server.start(function() {
console.log('Server running on host: ' + server.info.uri);
});

Should I have different method calls for my RESTful API?

I'm writing a web application on using Express. It has a RESTful API. The authentication schemes are different for the web interface and the RESTful API. Now I have something like this. The following code has the auth mechanisms stripped off.
problem.js
// Web
exports.list = function(req, res) {
listProblems(function(err, problems) {
if(err) {
res.send(err);
} else {
res.render('problems', { title: 'Problems', problems: problems });
}
});
};
// REST API
exports.apiList = function(req, res) {
listProblems(function(err, problems) {
if(err) {
res.send(err);
} else {
res.json(problems);
}
});
};
// Named function to query MongoDB, common to both Web and REST
function listProblems(callback) {
var query = Problem.find().select(DEFAULT_FIELDS);
query.exec(callback);
};
Routes in app.js
app.get('/problems', problem.list);
app.get('/api/v1/problems', problem.apiList);
I'm not sure if I'm doing the right thing. Please let me know. Many thanks in advance.

Nodeunit not detecting done()

Trying to get up to speed with node.js and nodeunit but am finding an issue with nodeunit where it's not seeing the call to test.done() in one of the tests.
The code:
// Added for clarity.
var client = require("restify").createJsonClient({
"version": "*",
"url": "http://localhost:" + server.Port
});
exports["tests"] = {
"setUp": function (callback) {
server.StartServer();
callback();
},
"tearDown": function (callback) {
callback();
},
"CanIHaveSomeTeaPlease?": function (test) {
test.expect(4);
client.get("/tea", function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
},
// Note: I expect this test to fail as it is a copy of the above
// test on a different url that doesn't return the ImATeapot
// HTTP error. But it doesn't look like it's detecting it
// properly.
"TakeThisInfo": function (test) {
test.expect(4);
client.put("/push", {
"hello": "world"
}, function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
}
};
Output:
FAILURES: Undone tests (or their setups/teardowns):
- tests - TakeThisInfo
To fix this, make sure all tests call test.done()
I'm hoping it is something stupid.
Versions:-
Node: 0.10.21
NPM: 1.3.11
Nodeunit: 0.8.2
Grunt-CLI: 0.1.10
Grunt: 0.4.1
First, I don't know what "server" is in your code, but I would expect it to be asynchronous, so to have something more like this in your setUp function:
function (callback) {
server.StartServer(function(){
callback();
});
}
Second, keep present that nodeunit executes the startUp and the tearDown functions after and before EVERY test so I suspect you are starting your server 2 times (as in the tearDown you are not really closing it).
I've spent the last couple of hours messing with this issue, and what has become clear is that nodeunit has no ability to catch and display exceptions thrown in functions which are triggered later by an IO or setTimeout type process. Considering the way JavaScript runs, this isn't surprising. Everything works once you are sure there are no exceptions, but if you have an error in your code, you will get "undone tests" message and nothing else. Here is what I did to resolve my issues (using a restify route as an example):
function myRoute(req, res, next) {
try {
// your code goes here...
}
catch (err) {
// write it to the console (for unit testing)
console.log(err);
// pass the error to the next function.
next(err);
}
}
Once I understood the problem in this way, fixing it because a lot more clear and I was able to get all of my tests to pass!
I suspect you're not actually calling test.done() in that second test. Put a console.log() call in there to verify you're actually making that call.
FWIW, I repro'd the described problem using a simplified version of your test, below. If you omit the on('error', function() {...}) handler, then the 2nd test fails to complete. Thus, my theory is that your /push endpoint is triggering a different behavior in the restify module. I.e. are you sure restify is invoking your callback with an err property there, or is it doing something different? ... like, for example, emitting an event like http.get does, below.
var http = require('http');
exports.test1 = function (test) {
test.expect(1);
http.get({hostname: "www.broofa.com", path: "/"}, function (res) {
test.equal(res.statusCode, 200, 'got 200');
test.done();
});
};
exports.test2 = function (test) {
test.expect(1);
http.get({hostname: "www.no-such-domain.com", path: "/"}, function (res) {
test.equal(res.statusCode, 200, 'got 200');
test.done();
}).on('error', function() {
// Comment line below out to repro the "Undone tests" error
test.done();
});
};
I'm working around it by forking the server into it's own process in the setup and then killing it in the teardown. Think the issue was to do with the server being created and not shutdown. Thanks #matteofigus for that.
var cp = null; // child process
exports["tests"] = {
"setUp": function (callback) {
cp = fork("./lib/server.js", {"silent": true});
callback();
},
"tearDown": function (callback) {
cp.kill("SIGHUP");
callback();
},
"CanIHaveSomeTeaPlease?": function (test) {
test.expect(4);
client.get("/tea", function (err, req, res, data) {
test.equal(err.statusCode, 418, "Expected ImATeapot Error.");
test.equal(err.message, "Want a biscuit?", "Expected to be asked if I want a buscuit.");
test.equal(err.restCode, "ImATeapotError");
test.equal(err.name, "ImATeapotError");
test.done();
});
},
"TakeThisInfo": function (test) {
test.expect(1);
client.put("/push", {
"hello": "world"
}, function (err, req, res, data) {
test.ok(false);
test.done();
});
}
};

Resources