简体   繁体   中英

Routing based on GET parameters for Express/Mongoose REST API

I'm trying to wrap my head around how to handle more complicated queries when designing a REST API using Mongo + Express. Unfortunately, all of the examples I've been able to find have been too simple. Here's a simple example for the purpose of this question. I have Groups and I have Users. Within each Group, there are members and 1 leader. For the sake of simplicity, I am going to exclude middleware and post/put/delete functionality.

The routes would look something like this:

app.get('/groups', groups.all);  
app.get('/groups/:groupId', groups.show);  
app.param('groupId', groups.group); 

The controller would look something like this:

/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
   Group = mongoose.model('Group'), 
   _ = require('lodash');


/**
* Find group by id
*/
exports.group = function(req, res, next, id) {
    Group.load(id, function(err, group) {
       if (err) return next(err);
       if (!group) return next(new Error('Failed to load group ' + id));
       req.group = group;
       next();
    });
};

/**
* Show a group
*/
exports.show = function(req, res) {
   res.jsonp(req.group);
};

/**
* List of Groups
*/
exports.all = function(req, res) {
   Group.find().sort('-created').populate('user', 'name username').exec(function(err, groups) {
    if (err) {
        res.render('error', {
            status: 500
        });
    } else {
        res.jsonp(groups);
    }
   });
};

And then the model would look something like this:

var mongoose = require('mongoose'),
Schema = mongoose.Schema;

/**
* Group Schema
*/
var GroupSchema = new Schema({
   created: {
       type: Date,
       default: Date.now
   },
   updated: {
       type: Date,
       default: Date.now
   },
   enableScores: {
       type: Boolean,
       default: false
   },
   name: {
       type: String,
       default: '',
       trim: true
   },
   creator: {
       type: Schema.ObjectId,
       ref: 'User'
   },
   commissioner: {
       type: Schema.ObjectId,
       ref: 'User'
   }
});

/**
* Validations
*/
GroupSchema.path('name').validate(function(name) {
   return name.length;
}, 'Name cannot be blank');

/**
* Statics
*/
GroupSchema.statics.load = function(id, cb) {
    this.findOne({
       _id: id
    })
    .populate('creator', 'name username')
    .populate('commissioner', 'name username') 
    .exec(cb);
};

mongoose.model('Group', GroupSchema);

What if I want to query the REST API based off the commissioner field? Or the creator, name, or created field? Is this possible using Express's routing and if so, is there a best practice?

It seems like instead of handling each of these unique situations, it would be better to have something generic that returns all groups that match based off of req.params because if the model changes later down the line, I won't need to update the controller. If this is the way to do it, then perhaps modifying the all() function to find based off of the req.params is the solution? So if nothing is provided, then it returns everything, but as you provide more get parameters, it drills down on what you are looking for.

I would recommend using req.query for matching fields in your schema. If you send a request like /groups?name=someGrp&enableScores=1 the req.query will look like this...

{name: "someGrp", enableScores: 1}

You can pass this object to the find method like this...

Group.find(req.query, callback);

This approach will work for simple property matching queries but for other things like comparisons and array properties you will have to write additional code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM