Simple CRUD JSON Server using Node.js

First lets get started by understanding what is Node.js. In simple terms, Node.js is a server side JavaScript technology. One can write a full server program (just like what we do in ASP.Net, PHP, JSP etc.,) and can host it on any platform on interest. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications. Node.js is primarily used to build fast, scalable network applications.

In this tutorial, I am going to show on how create a simple JSON CRUD Server in Node.js. Lets get started by creating a New Node.js Project in Visual Studio 2013.

image

It will create the basic structure for Node.js project in Visual Studio, as shown below.

image

Open Server.js file and we can start writing code.

First we need to import Node.js HTTP and URL Modules (URL Module is not necessary, but I will use this module to parse request Url and obtain certain information in later part of tutorial). HTTP module is used for creating an HTTP Server program. URL module helps in parsing Request URL and its querystring.

// Load modules
var http = require('http');
var url = require('url');

To demonstrate CRUD operations in this tutorial, I am going to take a simple vehicle object. In this tutorial, we are going work with in-memory vehicle collections array. One can easily extend this to database related operations (DB operations are out of scope for this articles, sooner I will come up with some other tutorials involving DB operations).

// Vehicle Object
var vehicle = function (name, imageUrl) {
    this.Name = name;
    this.ImageUrl = imageUrl;
}

// Existing Vehicles Repository
var vehicles = [];
vehicles.push(new vehicle('Mercedes', 'Merc.png'));
vehicles.push(new vehicle('Nissan', 'Nissan.png'));

Now lets create a helper API to our CRUD operations.

// Get all Vehicles
var getVehicles = function (callback) {
    callback(null, vehicles);
};

// Get a Vehicle by name
var getVehicleByName = function (name, callback) {
    var filteredVehicles = [];
    for (var i = 0; i < vehicles.length; i++) {
        if (vehicles[i].Name.toUpperCase() === name.toUpperCase()) {
            filteredVehicles.push(vehicles[i]);
        }
    }
    if (filteredVehicles.length != 0)
        callback(null, filteredVehicles);
    else
        callback(invalidOperationException());
};

// Create a Vehicle
var createVehicle = function (newVehicle, callback) {
    vehicles.push(newVehicle);
    
    if (vehicles.length != 0)
        callback(null, newVehicle);
    else {
        callback(invalidOperationException());
    }
};

// Delete a Vehicle by name
var deleteVehicleByName = function (name, callback) {
    for (var i = 0; i < vehicles.length; i++) {
        if (vehicles[i].Name.toUpperCase() === name.toUpperCase())
            vehicles.splice(i, 1);
    }

    if (vehicles.length != 0)
        callback(null, vehicles);
    else
        callback(invalidOperationException());
};

// Update a Vehicle by name
var updateVehicleByName = function (name, updateVehicle, callback) {
    for (var i = 0; i < vehicles.length; i++) {
        if (vehicles[i].Name.toUpperCase() === name.toUpperCase())
            vehicles[i] = updateVehicle;
    }

    if (vehicles.length != 0)
        callback(null, updateVehicle);
    else {
        callback(invalidOperationException());
    }
};

In case of exceptions, lets create helper functions to handle errors and return appropriate error messages.

// Error Handler
function generateError(err, msg) {
    var error = new Error(msg);
    error.code = err;
    return error;
}

// Custom Exceptions
function notFoundException() {
    return generateError("404", "The requested resource does not exist.");
}
function invalidOperationException() {
    return generateError("400", "The specified vehicle does not exist");
}
function missingInformationException() {
    return generateError("400", "Important information of vehicle does not exist");
}

Lets create SUCCESS and FAILURE functions, through which a response will be sent to a corresponding request. The scope of this tutorial is to handle JSON requests and responses.

// Success function
function success(response, data) {
    response.writeHead(200, {
        "Content-Type" : "application/json"
    });
    
    response.end(JSON.stringify({
        error : null,
        data : data
    }));
}

// Failure function
function failure(response, err) {
    response.writeHead(err.code, {
        "Content-Type" : "application/json"
    });
    response.end(JSON.stringify({
        error : err.code,
        message : err.message
    }));
}

We need a function to parse incoming Request body and convert to the appropriate Vehicle object, that function is demonstrated below.

// Parse POST Request Body
function parseRequestBody(request, callback) {
    var body = '';
    request.on(
        'readable' ,
        function () {
            var rawBody = request.read();
            if (rawBody) {
                if (typeof rawBody == 'string') {
                    body += rawBody;
                } else if (typeof rawBody == 'object' && rawBody instanceof Buffer) {
                    body += rawBody.toString('utf8');
                }
            }
        });

    request.on(
        'end',
        function() {
            if (body) {
                try {
                    var vehicleData = JSON.parse(body);
                    if (!vehicleData.Name) {
                        callback(missingInformationException());
                        return;
                    }
                    callback(null, vehicleData);
                } catch (e) {
                    callback(invalidOperationException());
                    return;
                }
            }
        });
}

The heart of the complete server is the following function, which will handle all incoming requests and generate JSON responses. This function will use all above helper functions.

// Main handler to process incoming request
function processRequest(request, response) {
    request.parsedurl = url.parse(request.url, true);
    var requestUrl = request.parsedurl.pathname;
    
    // Handle Request for all vehicles
    if (requestUrl == '/allvehicles.json' && request.method == 'GET') {
        getVehicles(function (err, vehicles) {
            if (err) {
                failure(response, err);
                return;
            }
            success(response, vehicles);
        });
    }
    // Handle Request for a specific Vehicle 
    else if (requestUrl.substr(0, 9) == '/vehicles' && requestUrl.substr(requestUrl.length - 5) == '.json' && request.method == 'GET') {
        getVehicleByName(requestUrl.substr(10, (requestUrl.lastIndexOf('.json') - 10)), function (err, vehicle) {
            if (err) {
                failure(response, err);
                return;
            }
            success(response, vehicle);
        });
    } 
    // Handle Request for a new Vehicle 
    else if (requestUrl.substr(0, 4) == '/new' && request.method == 'POST') {
        parseRequestBody(request, function (err, newVehicle) {
            if (err) {
                failure(response, err);
                return;
            }
            createVehicle(newVehicle, function(e, createdVehicle) {
                if (e) {
                    failure(response, e);
                    return;
                }
                success(response, createdVehicle);
            });
        });
    }   
    // Handle Update Request for a Vehicle 
    else if (requestUrl.substr(0, 7) == '/update' && requestUrl.substr(requestUrl.length - 5) == '.json' && request.method == 'PUT') {
        parseRequestBody(request, function (err, updateVehicle) {
            if (err) {
                failure(response, err);
                return;
            }
            updateVehicleByName(requestUrl.substr(8, (requestUrl.lastIndexOf('.json') - 8)), updateVehicle, function(e, updatedVehicle) {
                if (e) {
                    failure(response, e);
                    return;
                }
                success(response, updatedVehicle);
            });
        });
    }
    // Handle Delete Request for a Vehicle 
    else if (requestUrl.substr(0, 7) == '/delete' && requestUrl.substr(requestUrl.length - 5) == '.json' && request.method == 'GET') {
        deleteVehicleByName(requestUrl.substr(8, (requestUrl.lastIndexOf('.json') - 8)), function (err, vehicles) {
            if (err) {
                failure(response, err);
                return;
            }
            success(response, vehicles);
        });
    } else {
        failure(response, notFoundException());
    }
};

Finally we start the server as shown below.

// Start JSON Server
var server = http.createServer(processRequest);
server.listen(1337);

As a caution point, make sure we use the same port which was specified in the properties of Node.js VS Project (right click project –> Properties).

Press F5 to start run our JSON server program. Open /allvehicles.json in any local browser to see JSON response of all vehicles present on server.

image

Then enter /vehicles/nissan.json to see JSON response of a particular vehicle.

image

Lets open Fiddler (a free web debugger tool), and create following request and click execute.

image

If we look at the response, we get 200 OK success message.

image

If you look at /allvehicles.json end point, we get newly added vehicle in the response.

image

Now lets delete a vehicle, by visiting /delete/nissan.json end point. Nissan vehicle will be delete from the array of vehicles as shown below.

image

Last but not least, lets update Mercedes vehicles imageUrl from Merc.png to Mercedes.png. For that we need create a request as shown below.

image

Once above request has been executed, we can hit our all vehicles end point to see updated list.

image

Stay tuned for more interesting tutorials in Node.js, till then Happy Coding!!!

You may also like...

  • Bhagat Prasad

    Hi Rami ,

    am new to node.js am try to do some crud operations using node.js and sql server but am aneble to do that please expline me step by step for all curd operations using sql server and node.js .
    my mail id XXX@gmail.com

  • ramiramilu

    @bhagat_prasad:disqus i will try my best to put some tutorials around Node.Js and SQL Server in future. Stay Tuned :-).