In simple terms, REST stands for Representational State Transfer. It is an architectural style design for distributed hypermedia, or an Application Programming Interface (API). In REST, we use various standard HTTP
methods like GET
, POST
, PUT
and DELETE
to perform any CRUD
operation on resource.
In REST
, everything is Resource
. A resource can be an image, document, a temporary service, a collection of other resource, and any other object. Each resource has resource identifier to identify it.
As per REST
guidelines, we should use only HTTP
methods to perform CRUD
operation on any resource. In this blog, we are going to use 4 HTTP methods like GET
, POST
, PUT
and DELETE
to make our REST
API.
Let’s have a brief introduction of each http method here.
The HTTP GET
is used to Read
or Retrieve
any resource. It returns the XML or JSON data with HTTP status code of 200. GET method is considered as safe, because we are just getting or reading the resource data, not doing any changes in the resource data.
The HTTP POST
is used to Create
a new resource. On successful creation of resource, it will return HTTP status code of 201, a Location header with a link to the newly created resource.
The HTTP PUT
is used to Update
any existing resource. On successful, it will return HTTP status code of 200.
The HTTP DELETE
, as the name suggests, is used to Delete
any existing resource. On successful, it will return HTTP status code of 200.
HTTP MethodsCRUDStatus CodeGETRead200 (OK), 404 (Not Found)POSTCreate201 (OK), 404 (Not Found), PUTUpdate200 (OK), 204 (No Content), 404 (Not Found)DELETEDelete200 (OK), 404 (Not Found)Let’s move forward into the details of other pieces of creating our REST API.
We are going to use Express.js or simply Express. It is a web application framework for Node.js. It has been released as free and open source software. You can create web application and APIs using Express. It has support for routing, middleware, view system etc.
Mongoose is Object Document Mapping or ODM tool for Node.js and MongoDB. Mongoose provide a straight-forward, schema based solution to model to your application data. It includes built-in type casting, validation, query building, business logic hooks and many more.
You must have Node.js and MongoDB installed on your machine. Click the below links, if you don’t have any one of them.
For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.
Database-as-a-Service for MongoDB
Install Postman – Google Chrome for testing purpose.
After setting up prerequisites, let move forward to build our application.
In our application, we are going to create a product based application. We will use REST APIs to create, update, get and delete the product. Let’s go to create our application.
Let’s create a folder, and start with creating package.json
file first. Use this command in your terminal window.
For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.#### package.json
{
"name": "product-app",
"version": "1.0.0",
"description": "This is zepbook product app",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ZeptoBook",
"license": "MIT"
}
If you notice line 5, we have defined server.js
as our main entry point.
Let’s install all the express
, mongoose
and body-parser
package dependencies in our app.
For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.
Once these packages installed successfully, ourpackage.json
file will be updated automatically. Our latest file will be like this.
{
"name": "product-app",
"version": "1.0.0",
"description": "This is zepbook product app",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ZeptoBook",
"license": "MIT",
"dependencies": {
"body-parser": "^1.18.3",
"express": "^4.16.4",
"mongoose": "^5.4.2"
}
}
Notice dependencies
section of our file, all these packages are mentioned there.
Let’s create a server.js
file in the root directory of the application.
// get dependencies
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// parse requests
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// default route
app.get('/', (req, res) => {
res.json({"message": "Welcome to ZeptoBook Product app"});
});
// listen on port 3000
app.listen(3000, () => {
console.log("Server is listening on port 3000");
});
view raw
Let’s briefly review our above code. First of all, we imported the required dependencies in our server.js file.
It is Node.js body parser middleware. It parse the incoming request bodies in a middleware before your handlers, available under the req.body
property.
Learn more about bodyParser.urlencoded([options])
Learn more about bodyParser.json([options])
Then, we define a default route using GET Http method. By default, it will return our message on default url.
Finally, we are going to listen all incoming requests on port 3000.
Once everything is all set, let’s wake up our server by running this command in our terminal window.
For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.> For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.#### 5. Create Configuration file
Let’s create a config file in our app, where we can define various constants like dbconnection or port number instead of hard-coded it everywhere. So, create a config.js
file in your app folder.
module.exports = {
url: 'mongodb://<dbUserName>:<dbUserPassword>@ds251002.mlab.com:51002/adeshtestdb',
serverport: 3000
}
So, here I mentioned two constants in config.js
file.
Let’s connect with our MongoDb database. Add these lines of codes in server.js
file after the app.use(bodyParser.json());
// Configuring the database
const config = require('./config.js');
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
// Connecting to the database
mongoose.connect(config.url, {
useNewUrlParser: true
}).then(() => {
console.log("Successfully connected to the database");
}).catch(err => {
console.log('Could not connect to the database. Exiting now...', err);
process.exit();
});
Also, we are going to replace our hard-coded server port with our config constant in server.js file
// listen on port 3000
app.listen(config.serverport, () => {
console.log("Server is listening on port 3000");
});
Here, you can see, we are now using config.serverport
in app.listen().
Now, run again the server using this command.
For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.> For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.> For MongoDB, I am using mLab free account for online MongoDB database. You can try this one as well, instead of installing on your local machine.#### 7. Creating Product Model
Let’s create a product
model in our app folder in order to save the data in our db. Create a product.model.js
file in your app.
const mongoose = require('mongoose');
const ProductSchema = mongoose.Schema({
title: String,
description: String,
price: Number,
company: String
}, {
timestamps: true
});
module.exports = mongoose.model('Products', ProductSchema);
Here, we have defined our ProductSchema
with following properties. Along with this, we also set timestamps
property to true
. This property will add two fields automatically to schema. These fields are : createdAt
and updatedAt
in your schema.
We are going to write all functions related to create, retrieve, update and delete products in our controller file. Let’s create a controller file named product.controller.js
in your app folder.
const Product = require('./product.model.js');
//Create new Product
exports.create = (req, res) => {
// Request validation
if(!req.body) {
return res.status(400).send({
message: "Product content can not be empty"
});
}
// Create a Product
const product = new Product({
title: req.body.title || "No product title",
description: req.body.description,
price: req.body.price,
company: req.body.company
});
// Save Product in the database
product.save()
.then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
message: err.message || "Something wrong while creating the product."
});
});
};
// Retrieve all products from the database.
exports.findAll = (req, res) => {
Product.find()
.then(products => {
res.send(products);
}).catch(err => {
res.status(500).send({
message: err.message || "Something wrong while retrieving products."
});
});
};
// Find a single product with a productId
exports.findOne = (req, res) => {
Product.findById(req.params.productId)
.then(product => {
if(!product) {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
res.send(product);
}).catch(err => {
if(err.kind === 'ObjectId') {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
return res.status(500).send({
message: "Something wrong retrieving product with id " + req.params.productId
});
});
};
// Update a product
exports.update = (req, res) => {
// Validate Request
if(!req.body) {
return res.status(400).send({
message: "Product content can not be empty"
});
}
// Find and update product with the request body
Product.findByIdAndUpdate(req.params.productId, {
title: req.body.title || "No product title",
description: req.body.description,
price: req.body.price,
company: req.body.company
}, {new: true})
.then(product => {
if(!product) {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
res.send(product);
}).catch(err => {
if(err.kind === 'ObjectId') {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
return res.status(500).send({
message: "Something wrong updating note with id " + req.params.productId
});
});
};
// Delete a note with the specified noteId in the request
exports.delete = (req, res) => {
Product.findByIdAndRemove(req.params.productId)
.then(product => {
if(!product) {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
res.send({message: "Product deleted successfully!"});
}).catch(err => {
if(err.kind === 'ObjectId' || err.name === 'NotFound') {
return res.status(404).send({
message: "Product not found with id " + req.params.productId
});
}
return res.status(500).send({
message: "Could not delete product with id " + req.params.productId
});
});
};
Next step is to create our api routes. Create a product.routes.js
file in your app folder.
module.exports = (app) => {
const products = require('./product.controller.js');
// Create a new Product
app.post('/products', products.create);
// Retrieve all Products
app.get('/products', products.findAll);
// Retrieve a single Product with productId
app.get('/products/:productId', products.findOne);
// Update a Note with productId
app.put('/products/:productId', products.update);
// Delete a Note with productId
app.delete('/products/:productId', products.delete);
}
Note: import this route file in our server.js file after these lines. See line 4 in the below code.
// Configuring the database
const config = require('./config.js');
const mongoose = require('mongoose');
require('./product.routes.js')(app); //Add route file he
server.js
If you try to access your api routes through your client-side app, you might face Access-Control-Allow-Origin
error messages. So, in order to avoid these message, we are also enabling CORS
in our server.js
file.
//Enable CORS for all HTTP methods
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
So, this will be our final server.js
file.
// get dependencies
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// parse requests
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//Enable CORS for all HTTP methods
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// Configuring the database
const config = require('./config.js');
const mongoose = require('mongoose');
require('./product.routes.js')(app);
mongoose.Promise = global.Promise;
// Connecting to the database
mongoose.connect(config.url, {
useNewUrlParser: true
}).then(() => {
console.log("Successfully connected to the database");
}).catch(err => {
console.log('Could not connect to the database. Exiting now...', err);
process.exit();
});
// default route
app.get('/', (req, res) => {
res.json({"message": "Welcome to ZeptoBook Product app"});
});
// listen on port 3000
app.listen(config.serverport, () => {
console.log("Server is listening on port 3000");
});
This will be our project structure.
Now, it’s time to test our all REST APIs for CRUD Operation.
I have added few products in the database. See the below screen shot.
Before Update
After Update
You can download all the project files from GitHub.
Further reading:
☞ Build a REST API to manage users and roles using Firebase and Node.js
☞ Using Node 11.7 Worker Threads With RxJS Observable
☞ How to build a command-line chat app using SocketIO
☞ Running a Node REPL Within Your App’s Environment
☞ Use MongoDB Node.js Native Driver Without Mongoose
☞ Deploying a Node 12 Function to Cloud Run
☞ Video Streaming with Node.js
#node-js #javascript