When talking to Node.js developers, it is common to hear about NoSQL as the database of choice for development. javascript and JSON come hand in hand because after all JSON stands for JavaScript Object Notation. This is a format most common in document oriented databases which Node.js developers tend to use.
A very popular stack of development technologies is the MongoDB, Express Framework, Angular, and Node.js (MEAN) stack, but similarly there is also the Couchbase, Express Framework, Angular, and Node.js (CEAN) stack. Now don’t get me wrong, every technology I listed is great, but when your applications need to scale and maintain their performance, you might have better luck withCouchbase because of how it functions by design.
So what if you’re already using MongoDB in your Node.js application?
Chances are you’re using Mongoose which is an Object Document Model (ODM) for interacting with the database. Couchbase also has an ODM and it is called Ottoman . The great thing about these two ODM technologies is that they share pretty much the same set of APIs, making any transition incredibly easy.
We’re going to see how to take a MongoDB with Mongoose driven Node.js application and migrate it to Couchbase, using Ottoman.
The RequirementsThis tutorial is going to be a little different because of all the technologies involved. We’re going to be building everything from scratch for simplicity, so the following are requirements and recommendations:
Couchbase Server 4.5+ MongoDB 3.4+ Node.js 6.0+We’re going to start by building a MongoDB with Mongoose RESTful API in Node.js, hence the Node.js and MongoDB requirement. Then we’re going to take this application and migrate it to Couchbase.
For purposes of this example, we won’t be seeing how to configure Node.js, MongoDB, or Couchbase Server.
Understanding our NoSQL Data ModelBoth MongoDB and Couchbase are document databases. One stores BSON data and the other stores JSON, however from the developer perspective they are incredibly similar. That said, let’s design a few models based around students attending courses at a school. The first model we create might be for actual courses, where a single course might look like the following:
{ "id": "course-1", "type": "course", "name": "Computer Science 101", "term": "F2017", "students": [ "student-1", "student-2" ] }In the above, notice that the course has a unique id, and we’ve defined it as being a course. The course has naming information as well as a list of students that are enrolled.
Now let’s say we want to define our model for student documents:
{ "id": "student-1", "type": "student", "firstname": "Nic", "lastname": "Raboy", "courses": [ "course-1", "course-25" ] }Notice that the above model has a similar format to that of the courses. What we’re saying here is that both documents are related, but still semi-structured. We’re saying that each course keeps track of its students and each student keeps track of their courses. This is useful when we try to query the data.
There are unlimited possibilities when it comes to modeling your NoSQL data. In fact there are probably more than one hundred ways to define a courses and students model, versus what I had decided. It is totally up to you, and that is the flexibility that NoSQL brings. More information on data modeling can be foundhere.
With a data model in mind, we can create a simple set of API endpoints using each MongoDB with Mongoose and Couchbase with Ottoman.
Developing an API with Express Framework and MongoDBBecause we’re, in theory, migrating away from MongoDB to Couchbase, it would make sense to figure out what we want in a MongoDB application first.
Create a new directory somewhere on your computer to represent the first part of our project. Within this directory, execute the following:
npminit --y npminstallexpressbody-parsermongodbmongoose --saveThe above commands will create a file called package.json that will keep track of each of the four project dependencies. The express dependency is for Express framework and the body-parser dependency allows for request bodies to exist in POST, PUT, and DELETE requests, all of which are common for altering data. Then mongodb and mongoose are required for working with the database.
The project we build will have the following structure:
app.js routes/ courses.js students.js models/ models.js package.json node_modules/Go ahead and create those directories and files if they don’t already exist. The app.js file will be the application driver where as the routes will contain our API endpoints and the models will contain the database definitions for our application.
Defining the Mongoose SchemasSo let’s work backwards, starting with the Mongoose model that will communicate with MongoDB. Open the project’s models/models.js file and include the following:
var Mongoose = require("mongoose"); var Schema = Mongoose.Schema; var ObjectId = Mongoose.SchemaTypes.ObjectId; var CourseSchema = new Mongoose.Schema({ name: String, term: String, students: [ { type: ObjectId, ref: StudentSchema } ] }); var StudentSchema = new Mongoose.Schema({ firstname: String, lastname: String, courses: [ { type: ObjectId, ref: CourseSchema } ] }); module.exports.CourseModel = Mongoose.model("Course", CourseSchema); module.exports.StudentModel = Mongoose.model("Student", StudentSchema);In the above we’re creating MongoDB document schemas and then creating models out of them. Notice how similar the schemas are to the JSON models that we had defined previously outside of the application. We’re not declaring an id and type because the ODM handles this for us. In each of the arrays we use a reference to another schema. What we’ll see at save is a document id, but we can leverage the querying technologies to load that id into actual data.
So how do we use those models?
Creating the RESTful API RoutesNow we want to create routing information, or in other words, API endpoints. For example, let’s create all the CRUD endpoints for course information. In the project’s routes/courses.js file, add the following:
var CourseModel = require("../models/models").CourseModel; var router = function(app) { app.get("/courses", function(request, response) { CourseModel.find({}).populate("students").then(function(result) { response.send(result); }, function(error) { response.status(401).send({ "success": false, "message": error}); }); }); app.get("/course/:id", function(request, response) { CourseModel.findOne({"_id": request.params.id}).populate("students").then(function(result) { response.send(result); }, function(error) { response.status(401).send({ "success": false, "message": error}); }); }); app.post("/courses", function(request, response) { va