Skip to content

Instantly share code, notes, and snippets.

@iksose
Last active September 21, 2023 06:39
Show Gist options
  • Save iksose/9401758 to your computer and use it in GitHub Desktop.
Save iksose/9401758 to your computer and use it in GitHub Desktop.
Creating a REST API using Node.js, Express, and MongoDB

###Creating a REST API using Node.js, Express, and MongoDB

####Installing Node.js

Go to http://nodejs.org, and click the Install button. Run the installer that you just downloaded. When the installer completes, a message indicates that Node was installed at /usr/local/bin/node and npm was installed at /usr/local/bin/npm. At this point node.js is ready to use. Let’s implement the webserver application from the nodejs.org home page. We will use it as a starting point for our project: a RESTful API to access data (retrieve, create, update, delete) in a wine cellar database.

Create a folder named nodecellar anywhere on your file system. In the wincellar folder, create a file named server.js. Code server.js as follows:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:3000/');

We are now ready to start the server and test the application:

To start the server, open a shell, cd to your nodecellar directory, and start your server as follows: node server.js

To test the application, open a browser and access http://localhost:3000.

####Installing Express

Express is a lightweight node.js web application framework. It provides the basic HTTP infrastructure that makes it easy to create REST APIs.

To install Express in the nodecellar application:

In the nodecellar folder, create a file named package.json defined as follows:

{
    "name": "wine-cellar",
    "description": "Wine Cellar Application",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
        "express": "3.x"
    }
}

Open a shell, cd to the nodecellar directory, and execute the following command to install the express module. npm install

A node_modules folder is created in the nodecellar folder, and the Express module is installed in a subfolder of node_modules.

Now that Express is installed, we can stub a basic REST API for the nodecellar application:

Open server.js and replace its content as follows:

var express = require('express');
 
var app = express();
 
app.get('/wines', function(req, res) {
    res.send([{name:'wine1'}, {name:'wine2'}]);
});
app.get('/wines/:id', function(req, res) {
    res.send({id:req.params.id, name: "The Name", description: "description"});
});
 
app.listen(3000);
console.log('Listening on port 3000...');
view rawserver.js hosted with ❤ by GitHub
Stop (CTRL+C) and restart the server:
node server

To test the API, open a browser and access the following URLs: Get all the wines in the database: http://localhost:3000/wines Get wine with a specific id (for example: 1): http://localhost:3000/wines/1

####Using Node.js Modules

In a large application, things could easily get out of control if we keep adding code to a single JavaScript file (server.js). Let’s move the wine-related code in a wines module that we then declare as a dependency in server.js.

In the nodecellar folder, create a subfolder called routes. In the routes folder create a file named wines.js and defined as follows:

exports.findAll = function(req, res) {
    res.send([{name:'wine1'}, {name:'wine2'}, {name:'wine3'}]);
};
 
exports.findById = function(req, res) {
    res.send({id:req.params.id, name: "The Name", description: "description"});
};

Modify server.js as follows to delegate the routes implementation to the wines module:

var express = require('express'),
    wines = require('./routes/wines');
 
var app = express();
 
app.get('/wines', wines.findAll);
app.get('/wines/:id', wines.findById);
 
app.listen(3000);
console.log('Listening on port 3000...');

Restart the server and test the APIs: Get all the wines in the database: http://localhost:3000/wines Get wine with a specific id (for example: 1): http://localhost:3000/wines/1 The next step is to replace the placeholder data with actual data from a MongoDB database.

####Installing MongoDB

To install MongoDB on your specific platform, refer to the MongoDB QuickStart. Here are some quick steps to install MongoDB on a Mac:

Open a terminal window and type the following command to download the latest release: curl http://downloads.mongodb.org/osx/mongodb-osx-x86_64-2.2.0.tgz > ~/Downloads/mongo.tgz

Note: You may need to adjust the version number. 2.2.0 is the latest production version at the time of this writing.

Extract the files from the mongo.tgz archive: cd ~/Downloads tar -zxvf mongo.tgz

Move the mongo folder to /usr/local (or another folder according to your personal preferences): sudo mv -n mongodb-osx-x86_64-2.2.0/ /usr/local/

(Optional) Create a symbolic link to make it easier to access: sudo ln -s /usr/local/mongodb-osx-x86_64-2.2.0 /usr/local/mongodb

Create a folder for MongoDB’s data and set the appropriate permissions: sudo mkdir -p /data/db sudo chown id -u /data/db

Start mongodb cd /usr/local/mongodb ./bin/mongod

You can also open the MongoDB Interactive Shell in another terminal window to interact with your database using a command line interface. cd /usr/local/mongodb ./bin/mongo

Refer to the MongoDB Interactive Shell documentation for more information.

####Installing the MongoDB Driver for Node.js

There are different solutions offering different levels of abstraction to access MongoDB from Node.js (For example, Mongoose and Mongolia). A comparaison of these solutions is beyond the scope of this article. In this, guide we use the native Node.js driver.

To install the the native Node.js driver, open a terminal window, cd to your nodecellar folder, and execute the following command:

`npm install mongodb

Implementing the REST API

The full REST API for the nodecellar application consists of the following methods:

Method URL Action GET /wines Retrieve all wines GET /wines/5069b47aa892630aae000001 Retrieve the wine with the specified _id POST /wines Add a new wine PUT /wines/5069b47aa892630aae000001 Update wine with the specified _id DELETE /wines/5069b47aa892630aae000001 Delete the wine with the specified _id To implement all the routes required by the API, modify server.js as follows:

var express = require('express'),
    wine = require('./routes/wines');
 
var app = express();
 
app.configure(function () {
    app.use(express.logger('dev'));     /* 'default', 'short', 'tiny', 'dev' */
    app.use(express.bodyParser());
});
 
app.get('/wines', wine.findAll);
app.get('/wines/:id', wine.findById);
app.post('/wines', wine.addWine);
app.put('/wines/:id', wine.updateWine);
app.delete('/wines/:id', wine.deleteWine);
 
app.listen(3000);
console.log('Listening on port 3000...');
var mongo = require('mongodb');
 
var Server = mongo.Server,
    Db = mongo.Db,
    BSON = mongo.BSONPure;
 
var server = new Server('localhost', 27017, {auto_reconnect: true});
db = new Db('winedb', server);
 
db.open(function(err, db) {
    if(!err) {
        console.log("Connected to 'winedb' database");
        db.collection('wines', {strict:true}, function(err, collection) {
            if (err) {
                console.log("The 'wines' collection doesn't exist. Creating it with sample data...");
                populateDB();
            }
        });
    }
});
 
exports.findById = function(req, res) {
    var id = req.params.id;
    console.log('Retrieving wine: ' + id);
    db.collection('wines', function(err, collection) {
        collection.findOne({'_id':new BSON.ObjectID(id)}, function(err, item) {
            res.send(item);
        });
    });
};
 
exports.findAll = function(req, res) {
    db.collection('wines', function(err, collection) {
        collection.find().toArray(function(err, items) {
            res.send(items);
        });
    });
};
 
exports.addWine = function(req, res) {
    var wine = req.body;
    console.log('Adding wine: ' + JSON.stringify(wine));
    db.collection('wines', function(err, collection) {
        collection.insert(wine, {safe:true}, function(err, result) {
            if (err) {
                res.send({'error':'An error has occurred'});
            } else {
                console.log('Success: ' + JSON.stringify(result[0]));
                res.send(result[0]);
            }
        });
    });
}
 
exports.updateWine = function(req, res) {
    var id = req.params.id;
    var wine = req.body;
    console.log('Updating wine: ' + id);
    console.log(JSON.stringify(wine));
    db.collection('wines', function(err, collection) {
        collection.update({'_id':new BSON.ObjectID(id)}, wine, {safe:true}, function(err, result) {
            if (err) {
                console.log('Error updating wine: ' + err);
                res.send({'error':'An error has occurred'});
            } else {
                console.log('' + result + ' document(s) updated');
                res.send(wine);
            }
        });
    });
}
 
exports.deleteWine = function(req, res) {
    var id = req.params.id;
    console.log('Deleting wine: ' + id);
    db.collection('wines', function(err, collection) {
        collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true}, function(err, result) {
            if (err) {
                res.send({'error':'An error has occurred - ' + err});
            } else {
                console.log('' + result + ' document(s) deleted');
                res.send(req.body);
            }
        });
    });
}

 
/*--------------------------------------------------------------------------------------------------------------------*/
// Populate database with sample data -- Only used once: the first time the application is started.
// You'd typically not find this code in a real-life app, since the database would already exist.
var populateDB = function() {
 
    var wines = [
    {
        name: "CHATEAU DE SAINT COSME",
        year: "2009",
        grapes: "Grenache / Syrah",
        country: "France",
        region: "Southern Rhone",
        description: "The aromas of fruit and spice...",
        picture: "saint_cosme.jpg"
    },
    {
        name: "LAN RIOJA CRIANZA",
        year: "2006",
        grapes: "Tempranillo",
        country: "Spain",
        region: "Rioja",
        description: "A resurgence of interest in boutique vineyards...",
        picture: "lan_rioja.jpg"
    }];
 
    db.collection('wines', function(err, collection) {
        collection.insert(wines, {safe:true}, function(err, result) {});
    });
 
};

Restart the server to test the API.

Testing the API using cURL

If you want to test your API before using it in a client application, you can invoke your REST services straight from a browser address bar. For example, you could try:

http://localhost:3000/wines You will only be able to test your GET services that way. A more versatile solution to test RESTful services is to use cURL, a command line utility for transferring data with URL syntax.

For example, using cURL, you can test the Wine Cellar API with the following commands:

Get all wines: curl -i -X GET http://localhost:3000/wines

Get wine with _id value of 5069b47aa892630aae000007 (use a value that exists in your database): curl -i -X GET http://localhost:3000/wines/5069b47aa892630aae000007

Delete wine with _id value of 5069b47aa892630aae000007: curl -i -X DELETE http://localhost:3000/wines/5069b47aa892630aae000007

Add a new wine: curl -i -X POST -H 'Content-Type: application/json' -d '{"name": "New Wine", "year": "2009"}' http://localhost:3000/wines

Modify wine with _id value of 5069b47aa892630aae000007: curl -i -X PUT -H 'Content-Type: application/json' -d '{"name": "New Wine", "year": "2010"}' http://localhost:3000/wines/5069b47aa892630aae000007

@varmarakesh
Copy link

got this error
Error: .post() requires callback functions but got a [object Undefined]

@oliveirajorge88
Copy link

whenever I do a operation that requires the id, I get,

TypeError: Cannot read property 'findOne' of undefined

In the meanwhile solved it following this instructions:

http://stackoverflow.com/questions/10929443/nodejs-mongodb-getting-data-from-collection-with-findone

@rmrgh
Copy link

rmrgh commented Feb 22, 2016

@varmarakesh The mongodb calls must be in the routes/wines.js file.

@Cantabrucius
Copy link

@ramirio @varmarakesh, I have the same problem. Typing wine = require('./routes/wines.js'); doesn't work.

@nickmaine
Copy link

Thanks for the great post, I'm up and running.

Copy link

ghost commented Jun 7, 2016

You can use POSTMAN to test the REST API:
https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en

to fix the "Cannot read property 'findOne' of undefined":
http://stackoverflow.com/a/14551362

This is a good tutorial , Thanks

@wbj
Copy link

wbj commented Jun 25, 2016

In Express 4, most middleware has been separated out from Express core. Thus, modules that are required by your app must be explicitly added. See http://expressjs.com/en/guide/migrating-4.html for details.

I made the following changes to server.js to get it to work for me in the Express 4.x context:

This...

var express = require('express'),
    wine = require('./routes/wines');
var app = express();
app.configure(function () {
    app.use(express.logger('dev'));     /* 'default', 'short', 'tiny', 'dev' */
    app.use(express.bodyParser());
});

becomes ...

var express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan');
var wine = require('./routes/wines');
var app = express();
app.use(morgan('dev')); /* 'default','short','tiny','dev' */
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json());

note the change from "logger" in Express 3 to "morgan" in Express 4

I also added to my dependencies in Package.json:

"dependencies": {
    "body-parser": "^1.15.2",
    "express": "^4.14.0",
    "mongodb": "^2.1.18",
    "morgan": "^1.7.0"
 }  

... followed by "npm install"

Thanks to iksose for writing the original post, and to all the other commentators for contributing to the conversation.

@luhfluh
Copy link

luhfluh commented Aug 3, 2016

Great gist/tut, this got me initiated into MEN's (Mongo, Express, Node) club :)

@ntscuong1234
Copy link

i have the same code

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json());

but i get the message : app.use is not a function.
please, help me

@amitasthana
Copy link

I am contstantly getting this error.

module.js:440
throw err;
^

Error: Cannot find module './routes/wines'
at Function.Module._resolveFilename (module.js:438:15)
at Function.Module._load (module.js:386:25)
at Module.require (module.js:466:17)
at require (internal/module.js:20:19)
at Object. (/Users/amitasthana/nodeceller/server.js:4:12)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:456:32)
at tryModuleLoad (module.js:415:12)
at Function.Module._load (module.js:407:3)

I have tried multiple things in comments, still not able to resolve the isssue. I used the format suggested by @wbj in the post. Any help would be highly appreciated. Am I missing something here. Thanks

@aatifbandey
Copy link

how to start mongodb i have installed mongodb driver for nodejs

@aatifbandey
Copy link

do i need to install mongodb too i have already installed mogodb driver for Nodejs ?

@darthhomme
Copy link

How do I write a routing for put? I'm having a hard time with the callbacks.

@shamique
Copy link

Its worked for me. Few changes you need to make in the code.

Modify the server.js as follow,

var express = require('express'),
wine = require('./routes/wines');

var app = express();

app.get('/wines', wine.findAll);
app.get('/wines/:id', wine.findById);
app.post('/wines', wine.addWine);
app.put('/wines/:id', wine.updateWine);
app.delete('/wines/:id', wine.deleteWine);

app.listen(3000);
console.log('Listening on port 3000...');

and wine.js as follow,

var mongo = require('mongodb');

var Server = mongo.Server,
Db = mongo.Db,
BSON = mongo.BSONPure;

var server = new Server('localhost', 27017, {auto_reconnect: true});
db = new Db('winedb', server);

db.open(function(err, db) {
if(!err) {
console.log("Connected to 'winedb' database");
db.collection('wines', {strict:true}, function(err, collection) {
if (err) {
console.log("The 'wines' collection doesn't exist. Creating it with sample data...");
populateDB();
}
});
}
});

exports.findById = function(req, res) {
var id = req.params.id;
console.log('Retrieving wine: ' + id);
db.collection('wines', function(err, collection) {
collection.findOne({'_id':new BSON.ObjectID(id)}, function(err, item) {
res.send(item);
});
});
};

exports.findAll = function(req, res) {
db.collection('wines', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
};

exports.addWine = function(req, res) {
var wine = req.body;
console.log('Adding wine: ' + JSON.stringify(wine));
db.collection('wines', function(err, collection) {
collection.insert(wine, {safe:true}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred'});
} else {
console.log('Success: ' + JSON.stringify(result[0]));
res.send(result[0]);
}
});
});
}

exports.updateWine = function(req, res) {
var id = req.params.id;
var wine = req.body;
console.log('Updating wine: ' + id);
console.log(JSON.stringify(wine));
db.collection('wines', function(err, collection) {
collection.update({'_id':new BSON.ObjectID(id)}, wine, {safe:true}, function(err, result) {
if (err) {
console.log('Error updating wine: ' + err);
res.send({'error':'An error has occurred'});
} else {
console.log('' + result + ' document(s) updated');
res.send(wine);
}
});
});
}

exports.deleteWine = function(req, res) {
var id = req.params.id;
console.log('Deleting wine: ' + id);
db.collection('wines', function(err, collection) {
collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred - ' + err});
} else {
console.log('' + result + ' document(s) deleted');
res.send(req.body);
}
});
});
}

/--------------------------------------------------------------------------------------------------------------------/
// Populate database with sample data -- Only used once: the first time the application is started.
// You'd typically not find this code in a real-life app, since the database would already exist.
var populateDB = function() {

var wines = [
{
    name: "CHATEAU DE SAINT COSME",
    year: "2009",
    grapes: "Grenache / Syrah",
    country: "France",
    region: "Southern Rhone",
    description: "The aromas of fruit and spice...",
    picture: "saint_cosme.jpg"
},
{
    name: "LAN RIOJA CRIANZA",
    year: "2006",
    grapes: "Tempranillo",
    country: "Spain",
    region: "Rioja",
    description: "A resurgence of interest in boutique vineyards...",
    picture: "lan_rioja.jpg"
}];

db.collection('wines', function(err, collection) {
    collection.insert(wines, {safe:true}, function(err, result) {});
});

};

@kaidez
Copy link

kaidez commented Dec 9, 2016

FWIW, here's the original post.

@ajaypro
Copy link

ajaypro commented Dec 16, 2017

I'm getting an error db.open is not a function at Object.<anonymous> (F:\Dev\Node projects\winecellar\routes\wines.js:10:4) at Module._compile (module.js:635:30) at Object.Module._extensions..js (module.js:646:10) at Module.load (module.js:554:32) at tryModuleLoad (module.js:497:12) at Function.Module._load (module.js:489:3) at Module.require (module.js:579:17) at require (internal/module.js:11:18) at Object.<anonymous> (F:\Dev\Node projects\winecellar\server.js:2:8) at Module._compile (module.js:635:30)

Please help tried all methods not able to find out the reason.

@venkatesh131092
Copy link

I'm getting an error db.open is not a function at Object.<anonymous> (F:\Dev\Node projects\winecellar\routes\wines.js:10:4) at Module._compile (module.js:635:30) at Object.Module._extensions..js (module.js:646:10) at Module.load (module.js:554:32) at tryModuleLoad (module.js:497:12) at Function.Module._load (module.js:489:3) at Module.require (module.js:579:17) at require (internal/module.js:11:18) at Object.<anonymous> (F:\Dev\Node projects\winecellar\server.js:2:8) at Module._compile (module.js:635:30)

Please help tried all methods not able to find out the reason.

@shafiyal
Copy link

I'm getting an error db.open is not a function at Object.<anonymous> (F:\Dev\Node projects\winecellar\routes\wines.js:10:4) at Module._compile (module.js:635:30) at Object.Module._extensions..js (module.js:646:10) at Module.load (module.js:554:32) at tryModuleLoad (module.js:497:12) at Function.Module._load (module.js:489:3) at Module.require (module.js:579:17) at require (internal/module.js:11:18) at Object.<anonymous> (F:\Dev\Node projects\winecellar\server.js:2:8) at Module._compile (module.js:635:30)

Please help tried all methods not able to find out the reason.

this is coming because of the mongodb version
change mongodb version in package.json to
"dependencies": {
"express": "^4.17.1",
"mongodb": "^2.2.33"
}
execute npm install
That's it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment