Create a REST API in Node.js with MongoDB on Alibaba Cloud

By Alex Mungai Muchiri, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud’s incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

MongoDB is designed to scale and offers superior performance compared to conventional relational databases. Specifically, the NoSQL model on which Mongo is based solves some very important challenges associated with SQL schemas. They include:

  1. Impeccable handling of large sets of unstructured, structured, or rapidly-changing data structures.
  2. It is agile and allows iteration of schemas quickly.
  3. Perfectly-suited for flexible object-oriented programming.
  4. It is easy to scale-out across geographically distributed systems.

Accordingly, choosing MongoDB as the backend for your mobile application is a wise consideration. Notably, scaling with the number of users is important, and so is being able to iterate the database to accept varying data structures. Rather than using rows and columns, MongoDB stores data in documents thus allowing room for structural changes. On the whole, Mongo scalability is three-tier:

  1. Cluster Scale: database distribution across hundreds of nodes across data centers.
  2. Performance: perform more than 100,000+ R/W ops per second with strict latency
  3. Data scale: sustain more than 1 billion documents

In this article, we are going to explore how to create a powerful REST API in Node.js with MongoDB. Server configuration with Mongo is quite easy, all you need is have your Alibaba Cloud Elastic Compute Service (ECS) instance ready for work. In our case, we will be using Ubuntu 16.04 as our operating system.

Install MongoDB on Alibaba Cloud ECS

Package NameDescriptionmongodb-orgA metapackage that will automatically install the four component packages listed below.mongodb-org-serverContains the mongod daemon and associated configuration and init scripts.mongodb-org-mongosContains the mongos daemon.mongodb-org-shellContains the mongo shell.mongodb-org-toolsContains the following MongoDB tools: mongoimport bsondump, mongodump, mongoexport, mongofiles, mongorestore, mongostat, and mongotop.

The mongodb-org-server package provides an initialization script that starts mongod with the /etc/mongod.conf configuration file.

Use this link and follow the instructions to install MongoDB on ECS Ubuntu Server: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/

After installation, we can now proceed to build our simple REST API

REST APIs

  1. Analyze posted questions
  2. Read, create, edit and remove answers
  3. Upvote or downvote answers

In this example, the following will be applied.

Development environment

  1. Plain JavaScript
  2. Node.js
  3. Express (JS framework)
  4. MongoDB (Database)
  5. Yarn (package management)
  6. Visual Studio Code as editor
  7. Postman (to test API endpoints)

Dependencies

  1. Parser (parses the body of incoming endpoint requests)
  2. Express (activates the application)
  3. Nodemon (restarts server to effect changes)
  4. Mongoose (simplifies MongoDB interactions through object data models)
  5. Morgan (a middleware to log HTTP requests)
  6. Eslint with Airbnb (it is an extension to help creation of high quality code)

Summary of the Tutorial

  1. Using express to create API routes
  2. Modelling API data
  3. Contacting MongoDB using Mongoose
  4. Cleaning up and testing the API using Postman

Building API routes Using Express

The Basics

  1. Create a basic web server using express (express has a tutorial for this process)
  2. Ensure that the express middleware is set to be flexible
  3. Include a body parser to handle requests
  4. Incorporate the parser in the express middleware

Creating Routes for Questions

  1. Include both POST and GET routes to find questions and add new
  2. For specific questions, create a GET route.
router.get('/', (req, res) => {
res.json({ response: 'a GET request for LOOKING at questions' });
});
router.post('/', (req, res) => {
res.json({
response: 'a POST request for CREATING questions',
body: req.body
});
});
router.get('/:qID', (req, res) => {
res.json({
response: `a GET request for LOOKING at a special answer id: ${req.params.qID}`
});
});

Answer Routes

  1. Next, create a POST route to accept answers.
  2. Next, include PUT and DELETE to allow editing and deleting of answers
  3. Create a POST route for upvoting and downvoting answers (we shall use a simple vote in this tutorial)
router.post('/:qID/answers', (req, res) => {
res.json({
response: 'a POST request for CREATING answers',
question: req.params.qID,
body: req.body
});
});
router.put('/:qID/answers/:aID', (req, res) => {
res.json({
response: 'a PUT request for EDITING answers',
question: req.params.qID,
answer: req.params.aID,
body: req.body
});
});
router.delete('/:qID/answers/:aID', (req, res) => {
res.json({
response: 'a DELETE request for DELETING answers',
question: req.params.qID,
answer: req.params.aID,
body: req.body
});
});
router.post('/:qID/answers/:aID/vote-:dec', (req, res) => {
res.json({
response: 'a POST request for VOTING on answers',
question: req.params.qID,
answer: req.params.aID,
vote: req.params.dec,
body: req.body
});
});

Error Handlers

  1. The middleware will act as our ‘error-catcher’.
  2. After catching error 404, use the middleware to pass it to a custom handler that responds with JSON response format (otherwise, use the 500 as shown below)
  3. Set up a validation middleware to allow error upvoting or downvoting.
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
message: err.message
}
});
});

Use Mongoose to Connect to MongoDB

Choose the Data Model for the API

Create Schemas

  1. Use your schemas to build a model
const AnswerSchema = new Schema({
text: String,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
votes: { type: Number, default: 0 }
});
const QuestionSchema = new Schema({
text: String,
createdAt: { type: Date, default: Date.now },
answers: [AnswerSchema]
});
const Question = mongoose.model('Question', QuestionSchema);

Incorporate Sorting and Voting into the Function

  1. Include a storage option for votes in the answer schema
  2. Instruct Mongoose to pre-sort all answers while saving
  3. Use the parent method to have answers referenced to the question parent document
  4. Avoid using arrow functions in the specification
const sortAnswers = (a, b) => {
if (a.votes === b.votes) {
return b.updatedAt - a.updatedAt;
}
return b.votes - a.votes;
};
QuestionSchema.pre('save', function (next) {
this.answers.sort(sortAnswers);
next();
});
AnswerSchema.method('update', function (updates, callback) {
Object.assign(this, updates, { updatedAt: new Date() });
this.parent().save(callback);
});
AnswerSchema.method('vote', function (vote, callback) {
if (vote === 'up') {
this.votes += 1;
} else {
this.votes -= 1;
}
this.parent().save(callback);
});

Link the API to MongoDB

Error Handling

  1. The method allows error identification when a question or answer is not available in the database.
router.param('qID', (req, res, next, id) => {
Question.findById(id, (err, doc) => {
if (err) return next(err);
if (!doc) {
err = new Error('Document not found');
err.status = 404;
return next(err);
}
req.question = doc;
return next();
});
});
router.param('aID', (req, res, next, id) => {
req.answer = req.question.answers.id(id);
if (!req.answer) {
err = new Error('Answer not found');
err.status = 404;
return next(err);
}
return next();
});

Getting the Questions Routes Ready

  1. It will return the appropriate questions.
  2. It will also post new questions in JSON format to the database using a POST route.
  3. Include a GET route for a single question.
router.get('/', (req, res, next) => {
Question.find({}).sort({ createdAt: -1 }).exec((err, questions) => {
if (err) return next(err);
res.json(questions);
});
});
router.post('/', (req, res) => {
const question = new Question(req.body);
question.save((err, question) => {
if (err) return next(err);
res.status(201);
res.json(question);
});
});
router.get('/:qID', (req, res) => {
res.json(req.question);
});

Routes to Update Answers

  1. User the update method to allow answer updating by returning the new JSON input.
  2. Accordingly, use the remove method when using the DELETE route to remove answers.
  3. For voting, we use a POST route embedded on the middleware to save votes in the vote method.

The example below illustrates this operation:

router.post('/:qID/answers', (req, res, next) => {
req.question.answers.push(req.body);
req.question.save((err, question) => {
if (err) return next(err);
res.status(201);
res.json(question);
});
});
router.put('/:qID/answers/:aID', (req, res, next) => {
req.answer.update(req.body, (err, result) => {
if (err) return next(err);
res.json(result);
});
});
router.delete('/:qID/answers/:aID', (req, res) => {
req.answer.remove(err => {
req.question.save((err, question) => {
if (err) return next(err);
res.json(question);
});
});
});
router.post(
'/:qID/answers/:aID/vote-:dec',
(req, res, next) => {
if (req.params.dec.search(/^(up|down)$/) === -1) {
const err = new Error(`Not possible to vot for ${req.params.dec}!`);
err.status = 404;
next(err);
} else {
req.vote = req.params.dec;
next();
}
},
(req, res, next) => {
req.answer.vote(req.vote, (err, question) => {
if (err) return next(err);
res.json(question);
});
}
);

Great! Now our REST API looks all set for consumption. We are now going to get into the next phase

Cleaning Up and Testing the Endpoints

Testing the API

Cross-Origin Resource Sharing

  1. The header should have access to all origins
  2. Enable HTTP requests
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
if (req.method === 'Options') {
res.header('Access-Control-Allow-Methods', 'PUT, POST, DELETE');
return res.status(200).json({});
}
});

Get the Front-end Working

Conclusion

Reference:

https://www.alibabacloud.com/blog/create-a-rest-api-in-node-js-with-mongodb-on-alibaba-cloud_593851?spm=a2c41.11826438.0.0

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.