Suppose I have the following schemas for a LMS app:
const CourseSchema = new mongoose.Schema({
name: { type: String, required: true },
code: { type: String, required: true, unique: 1, uppercase: 1 }, // ex. CSCA48
quizzes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Quiz' }]
});
const QuizSchema = new mongoose.Schema({
name: { type: String, required: true }, // ex. 1a
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
const QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // ex. 4
question: { type: String, required: true },
type: { type: String },
choices: [String],
answers: [String]
});
I built (or tried to build) the app following RESTful API guidelines. For example,
GET /courses/[ObjectID]
GET /courses/[ObjectID]/quizzes
GET /courses/[ObjectID]/quizzes/[ObjectID]
GET /courses/[ObjectID]/quizzes/[ObjectID]/questions
The reason why I used ObjectID was because they are unique and makes it easy to retrieve objects -- particularly with app.param(). For example,
router.param('courseID', controllers.Course.getCourseByParam);
router.param('quizID', controllers.Quiz.getQuizByParam);
router.param('questionID', controllers.Question.getQuestionByParam);
router.get('/courses', controllers.Course.getCourses);
router.get('/courses/:courseID', controllers.Course.getCourse);
router.get('/courses/:courseID/quizzes', controllers.Quiz.getQuizzes);
router.get('/courses/:courseID/quizzes/:quizID/questions', controllers.Question.getQuestions);
But now, I am wondering if there is a way to not use ObjectID in order make the URLs more friendly/readable?
GET /courses/[code]/quizzes/[name]/questions/[number]
ex. /courses/CSCA48/quizzes/1a/questions/4
My biggest concern with this is that, although course code
is unique, quiz name
and question number
are not. For example, /courses/CSCA48/quizzes/1b/questions/4
would be a different question for a different quiz. So, unless I am mistaken, it is not really possible to use app.param()
. Therefore I would need to somehow ensure that the quiz belongs to the matched course and then question belongs to the matched quiz.
I am not really looking for code, just a general idea of how I can approach this (or not).
Since the combination [code]-[name]-[number]
is unique (and [code]
too, which is important), you should be able to run a query that would bring up the correct question:
Course.findOne({ code : req.params.code })
.populate({
path : 'quizzes',
match : { name : req.params.name },
populate : {
path : 'questions',
match : { number : req.params.number }
}
})
Alternatively, you could add two fields to QuestionSchema
to reflect both the course and the quiz to which the question belongs, in which case you'd only have to run a single query to find the correct question document (population requires 3 queries).