Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
125 views
in Technique[技术] by (71.8m points)

How to use events keep mongodb logic out of node.js request handlers

I'm looking for a package (or pattern) to handle events from mongodb so I can avoid nested callbacks and keep mongodb logic out of my request handlers.

Right now I've got code that looks like this:

start-express.js (server)

var express = require('express');
var Resource = require('express-resource');
var app = express.createServer();

// create express-resource handler  which essentially does app.get('things', ...)
var things = app.resource('things', require('./things.js'));

app.listen(port);

things.js (express-resource request handler)

require('./things-provider');

// handle request  'http://example.com/things'
exports.index = function(request, response) {
    sendThings(db, response);
};

things-provider.js (handles mongodb queries)

var mongodb = require('mongodb')

// create database connection
var server = new mongodb.Server(host, port, {auto_reconnect: true});
var db = new mongodb.Db(dbName, server);

db.open(function (err, db) {
    if (err) { }
    // auto_reconnect will reopen connection when needed
});

function sendThings(db, response) {            
    db.collection('things', function(err, collection) {
        collection.find(function(err, cursor) {
            cursor.toArray(function(err, things) {
                response.send(things);
            });
        });
    });
}

module.exports.sendThings = sendThings;

I'd like to avoid passing my http response object to my database handler or (worse) handling my db request in my http response handler.

I recently realized that what I want to do is create an event handler that registers an http request/response and waits for a response (event) from database before processing and sending the http response.

That sounds like a lot of duplication of what node.js already does though. Is there an existing framework that handles this use case?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's the solution I've come up with.

I used mongojs which greatly simplifies the mongodb interface --at the cost of flexibility in configuration-- but it hides the nested callbacks the mongodb driver requires. It also makes the syntax much more like the mongo client.

I then wrap the HTTP Response object in a closure and pass this closure to the mongodb query method in a callback.

var MongoProvider = require('./MongoProvider');
MongoProvider.setCollection('things');

exports.index = function(request, response){
    function sendResponse(err, data) {
        if (err) { 
            response.send(500, err);
        }    
        response.send(data);
    };

    MongoProvider.fetchAll(things, sendResponse);
};

It is still essentially just passing the response object to the database provider, but by wrapping it in a closure that knows how to handle the response, it keeps that logic out of my database module.

A slight improvement is to use a function to create a response handler closure outside my request handler:

function makeSendResponse(response){
    return function sendResponse(err, data) {
        if (err) {
            console.warn(err);
            response.send(500, {error: err});
            return;
        }

        response.send(data);
    };
}

So now my request handler just looks like this:

exports.index = function(request, response) {
    response.send(makeSendResponse(response));
}

And my MongoProvider looks like this:

var mongojs = require('mongojs');

MongoProvider = function(config) {
this.configure(config);
    this.db = mongojs.connect(this.url, this.collections);
}

MongoProvider.prototype.configure = function(config) {
    this.url = config.host + "/" + config.name;
    this.collections = config.collections;
}

MongoProvider.prototype.connect = function(url, collections) {
    return mongojs.connect(this.url, this.collections);
}

MongoProvider.prototype.fetchAll = function fetchAll(collection, callback) {
    this.db(collection).find(callback);
}

MongoProvider.prototype.fetchById = function fetchById(id, collection, callback) {
    var objectId = collection.db.bson_serializer.ObjectID.createFromHexString(id.toString());

    this.db(collection).findOne({ "_id": objectId }, callback);
}

MongoProvider.prototype.fetchMatches = function fetchMatches(json, collection, callback) {
    this.db(collection).find(Json.parse(json), callback);
}

module.exports = MongoProvider;

I can also extend MongoProvider for specific collections to simplify the API and do additional validation:

ThingsProvider = function(config) {
    this.collection = 'things';
    this.mongoProvider = new MongoProvider(config);
    things = mongoProvider.db.collection('things');
}

ThingsProvider.prototype.fetchAll = function(callback) {
    things.fetchAll(callback);
}

//etc...

module.exports = ThingsProvider;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...