Way to save associations in mongoose without doing a save dance?

Problem

Say I have this schema.

   var Topic = new Schema({
        owner: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        },
        category: {
            type: Schema.Types.ObjectId,
            ref: 'Category'
        },
        title: String,
        date: Date,
        lastPost: Date,
        likes: Number,
        posts: [{
            type: Schema.Types.ObjectId,
            ref: 'Post'
        }]
    });

    var Post = new Schema({
        topic: {
            type: Schema.Types.ObjectId,
            ref: 'Topic'
        },
        body: String,
        date: Date,
        owner: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        }
    });

If I want to save the Topic then add the Topic to the topic association on the Post and then push to the posts array on the Topic object, I have to do this weird dance.

exports.create = function (req, res) {

    var data = req.body.data;

    var topic = new Topic();
    topic.title = data.topic.title;
    topic.date = new Date();
    topic.lastPost = new Date();

    topic.save(function (err, topicNew) {

        if (err) res.send(err, 500);

        var post = new Post();
        post.body = data.post.body;
        post.topic = topicNew;

        topicNew.posts.push(post);

        topic.save(function (err, t) {

            if (err) res.send(err, 500);
            post.save(function (err, p) {
                if (err) res.send(err, 500);
                return res.json(t);
            });
        });
    });
};

I'm not seeing anything in the documentation that would help me around this. Thanks for any help.

Problem courtesy of: Drew H

Solution

Instantiate both the topic and the post initially. Push the post into the topic before the first topic save. Then save the topic and if that succeeds save the post. MongoDB object IDs are created by the driver right when you do new Post() so you can save that in the topic.posts array before it's saved.

That will make your 3-step dance a 2-step dance, but in the grand scheme of things this seems essentially par for the course so I wouldn't set my expectations much lower than this. Very few useful real world routes can be implemented with a single DB command.

You can also use middleware functions as a way to get sequential async operations without nesting. You can use the req object to store intermediate results and pass them from one middleware to the next.

Solution courtesy of: Peter Lyons

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.