Build Node.js RESTful APIs in 10 Minutes
What is REST?
REST is an acronym for Representational State Transfer. It is web standards architecture and HTTP Protocol. The REST architectural style describes six constraints that were originally communicated by Roy Fielding in his doctoral dissertation and defines the basis of RESTful-style as:
- Uniform Interface
- Stateless
- Cacheable
- Client-Server
- Layered System
- Code on Demand (optional)
RESTful applications use HTTP requests to perform four operations termed as CRUD (C: create, R: read, U: update, and D: delete). Create and/or update is used to post data, get for reading/listing data, and delete to remove data.
RESTful is composed of methods such as; base URL, URL, media types, etc.
In this tutorial, we will learn how to create a RESTful API using Node.js.
Tools:
- Node.js
- MongoDB
- Text editor (Atom, Sublime, etc) (Read more: Best Text Editor? Atom vs Sublime vs Visual Studio Code vs Vim)
- Postman
Getting started
For the purpose of this tutorial, I’ll work you through creating a RESTful API. To achieve this, we will create a RESTful todo list API (i.e. endpoints that will create a task, get or read list of all tasks, read a particular task, delete a task, and update a task).
Assumptions
I presume that you already have your environment set up (i.e Node.js and MongoDB is installed).
Kindly run npm -v and mongo --version as these will show you the version of NPM and MongoDB installed on your machine.
If you don’t have it installed, kindly go through this link on how to install it in order for us to create a server in Node and Mongodb.
If you do have Node and MongoDB installed, let's begin the tutorial with the following basic steps.
Open your terminal and kindly follow the following steps
-
Create a Folder name todoListApi -
mkdir todoListApi -
Navigate to the root of your newly created folder -
cd todoListApi -
Create a package.json file -
npm init
Package.json is a file that gives the necessary information to npm which allows it to identify the project as well as handle the project's dependencies.
npm init will prompt you to enter some information such as the app name, description, version, author, keyword and also ask if what you see is what you like.
You should have something like this eventually.
Kindly type yes and press enter to complete the creation of our package.json.
Having done all these, your folder structure should look like this:
-
Create a file called server.js -
touch server.js.
In this server, we will writing the protocols to create our server. -
Create a folder called api -
mkdir api
Inside this folder called api, create three separate folders called models, routes, and controllers by runningmkdir api/controllers api/models api/routes
-
Create todoListController.js in the api/controller folder, todoListRoutes.js in the routes folder, and todoListModel in the model folder -
touch api/controllers/todoListController.js api/models/todoListModel.js api/routes/todoListRoutes.js
Our folder structure should look like this now:
Server setup
Let's install express and nodmon, express will be used to create the server while nodmon will help us to keep track of changes to our application by watching changed files and automatically restart the server.
npm install --save-dev nodemon
npm install express --save
On successful installation, your package.json file will be modified to have the two newly installed packages.
- Open the package.json file and add this task to the script
"start": "nodemon server.js"
- Open the server.js file and type/copy the code below into it
var express = require('express'),
app = express(),
port = process.env.PORT || 3000;
app.listen(port);
console.log('todo list RESTful API server started on: ' + port);
3.On your terminal, run npm run start this will start the server and then you will see
todo list RESTful API server started on: 3000
Setting up the schema
First of all, let’s install mongoose - npm install mongoose --save
Why Mongoose?
Mongoose is what we will use to interact with a MongoDB(Database) instance.
After installation, open the todoListModel.js file in your api/models folder and type the following code into the file and save.
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TaskSchema = new Schema({
name: {
type: String,
Required: 'Kindly enter the name of the task'
},
Created_date: {
type: Date,
default: Date.now
},
status: {
type: [{
type: String,
enum: ['pending', 'ongoing', 'completed']
}],
default: ['pending']
}
});
module.exports = mongoose.model('Tasks', TaskSchema);
From the code above, we required the mongoose in our file and then, we create a model of how our collection should look like.
As you can see, it the task collection(table) will contain a name: a string, and the date it was created. It also contains task status which we have defined as pending - a default value for every task created.
Setting up the routes
Routing refers to determining how an application responds to a client request for a specific endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).
Each of our routes has different route handler functions, which are executed when the route is matched.
Below we have defined two basic routes(‘/tasks’, and ‘/tasks/taskId’) with different methods
‘/tasks’ has to methods(‘GET’ and ‘POST’), while ‘/tasks/taskId’ has GET, PUT and DELETE.
As you can see, we required the controller so each of the routes methods can call it’s respective handler function.
'use strict';
module.exports = function(app) {
var todoList = require('../controllers/todoListController');
// todoList Routes
app.route('/tasks')
.get(todoList.list_all_tasks)
.post(todoList.create_a_task);
app.route('/tasks/:taskId')
.get(todoList.read_a_task)
.put(todoList.update_a_task)
.delete(todoList.delete_a_task);
};
Setting up the controller
Open todoListController.js file with your text editor( Sublime, Atom e.t.c) and let’s deep dive into coding.
In this controller, we would be writing five(5) different functions namely: list_all_tasks, create_a_task, read_a_task, update_a_task, delete_a_task. We will exported each of the functions for us to use in our routes.
Each of these functions uses different mongoose methods such as find, findById, findOneAndUpdate, save and remove.
'use strict';
var mongoose = require('mongoose'),
Task = mongoose.model('Tasks');
exports.list_all_tasks = function(req, res) {
Task.find({}, function(err, task) {
if (err)
res.send(err);
res.json(task);
});
};
exports.create_a_task = function(req, res) {
var new_task = new Task(req.body);
new_task.save(function(err, task) {
if (err)
res.send(err);
res.json(task);
});
};
exports.read_a_task = function(req, res) {
Task.findById(req.params.taskId, function(err, task) {
if (err)
res.send(err);
res.json(task);
});
};
exports.update_a_task = function(req, res) {
Task.findOneAndUpdate(req.params.taskId, req.body, {new: true}, function(err, task) {
if (err)
res.send(err);
res.json(task);
});
};
exports.delete_a_task = function(req, res) {
Task.remove({
_id: req.params.taskId
}, function(err, task) {
if (err)
res.send(err);
res.json({ message: 'Task successfully deleted' });
});
};
Putting everything together
- Connect our database by adding a url to the mongoose instance connection
- Load the created model - task
- Install bodyParser and use
bodyParser Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
It exposes various factories to create middlewares. All middlewares will populate the req.bodyproperty with the parsed body, or an empty object ({}) if there was no body to parse (or an error was returned). - Register our created routes in the server
var express = require('express'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
Task = require('./api/models/todoListModel'),
bodyParser = require('body-parser');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/Tododb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var routes = require('./api/routes/todoListRoutes');
routes(app);
app.listen(port);
console.log('todo list RESTful API server started on: ' + port);
5.Start MongoDB Server
Open your terminal and run mongod
This will start your MongoDB server and then, node server could connect to the MongoDB instance. Once your MongoDB server is running, restart your node server by running: rs on your nodemon running terminal.
Testing via Postman
Now that everything is now connected, let’s test each of the routes and the respective methods.
Open your postman and type:
- http://localhost:3000/tasks in the enter request URL section and press enter.
On enter, you should see “[]” because there is nothing in the database yet. - On the same address, change the method to POST, click body and select “x-www-form-urlencoded”.
Then, enter name as the key and the corresponding task name as value.
After this, click on send button.
This should give you a response 200 ok
Adding a middleware
Having done all these, what happens if we entered a wrong route? say you entered 'http://localhost:3000/task', It responds with a message “Cannot GET /task”. Let’s add express middleware which could be used to return more interactive messages.
Middlewares basically intercepts incoming http request and as such you can use them to perform several operations ranging from authentication to validations etc.
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ' not found'})
});
The snippet above helps to redirect and respond whenever a wrong route is entered on the site.
Hi Olatunde,
I am simply a punter trying to have a crack at getting this working.
It may sound like a silly question but the “putting it all together” section is confusing to me as it doesn’t have step by step instructions in terms of what to do, I managed to follow the tutorial up until here:
Putting everything together
Connect our database by adding a url to the mongoose instance connection
Load the created model - task
Install bodyParser and use
bodyParser Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
It exposes various factories to create middlewares. All middlewares will populate the req.bodyproperty with the parsed body, or an empty object ({}) if there was no body to parse (or an error was returned).
Register our created routes in the server
Can you provide further details as to how to complete this?
Hi Thomas
I’ve just pasted all the code replacing what is in the
server.jsfile and also installed body parser using the following line in the terminalThen restarted the server and everything goes fine
This was excellent, thank you for the write-up. I already have mine hosted on my heroku site and works in Postman!
Thank you Olatunde for such an helpful tutorial. One thing that is not working for me correctly is introducing
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ’ not found’})
});
When I introduced above middleware, my response to correct call like http://localhost:3000/tasks was also coming back as above msg till I commented it so not sure what’s going on?
Here are my env configurations:
node: 7.9.0
npm: 4.5.0
“devDependencies”: {
“nodemon”: “^1.11.0”
},
“dependencies”: {
“body-parser”: “^1.17.2”,
“express”: “^4.15.3”,
“mongoose”: “^4.10.0”
}
Hi! I moved app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ’ not found’})
}); in server.js file
after app.listen(port); line and it worked correctly.
This is super helpful. Thanks!
I ran into an error on the server setup:
port = process.env.PORT || 300;
should be:
port = process.env.PORT || 3000;
Great article… very helpful… suppose i were to use firebase for database, how would the code for model defer?
Perfect…! Thank you…!
Brilliant! Thanks!
thank you very much
Hi Olatunde, very helpful article. I found that update_a_task won’t work like it should because it’s missing an object so instead passing only taskId you should change query in findOneAndUpdate method to { _id : req.params.taskId}, like in delete_a_task. Please let me know what do you think.
Hi Kriz, the query param in findOneAndUpdate could be String/ObjectId/Object.
hi olatunde thanks for this! The get and post work perfectly, however how do you use the put,get,delete using the taskid?
Chiming in to hopefully answer your question, when you get all tasks with ‘/tasks’ route, each task will have an _id field, and you can use the value of the field to form PUT, GET, DELETE request for a particular task, for example, for me, I can use POSTMAN to issue request to ‘http://localhost:3000/tasks/58df9f456368e10907e41eb9’, where 58df9f456368e10907e41eb9 is the _id field of one of the tasks.
Great tutorial.
I only had one problem with body-parser when i tried to run, but i fixed that installing the body parser with npm install command.
very frustrating:
when i hit npm run start :
nodemon server.js
[nodemon] 1.11.0
[nodemon] to restart at any time, enter
rs[nodemon] watching: *.*
[nodemon] starting
node server.jsC:\Program Files\MongoDB\Server\3.4\nodeapi\todoListApi\server.js:5
app.listen(port);
^
SyntaxError: Unexpected token .
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:542:28)
at Object.Module._extensions…js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
[nodemon] app crashed - waiting for file changes before starting…
When I try node server.js:
app.listen(port);
^
SyntaxError: Unexpected token .
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:542:28)
at Object.Module._extensions…js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
can you show me the file in your server.js?
var express = require(‘express’),
app = express(),
port = process.env.PORT || 3000;
app.listen(port);
console.log('todo list RESTful API server started on: ’ + port);
//changed 3000, to 3000;
This should work btw.
so there is nothing wrong with the server.js? Can you tell me: can I put my project folder (todolistapi) anywhere I want, like on my desktop, or does it have to be somewhere in the mongoDB folder? also the mongo --version ONLY works in the mongoDB bin folder but not anywhere else.
I got this error. you need to run “npm init” not at todoListApi directory as said, but at the api directory. package.json and server.js must be at the same directory.
Amazing Tutorial… Thanks a Lot!
Hey Olatunde,
Disclaimer: I’m using this tutorial to build a product list API.
Everything worked until I tried to write the route for “.remove”. I keep getting the error below:
/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/api/controllers/productListController.js:40
_id: req.params.productId
^
ReferenceError: req is not defined
at Object.<anonymous> (/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/api/controllers/productListController.js:40:8)
at Module._compile (module.js:541:32)
at Object.Module._extensions…js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at module.exports (/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/api/routes/productListRoutes.js:3:21)
at Object.<anonymous> (/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/server.js:15:1)
at Module._compile (module.js:541:32)
at Object.Module._extensions…js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.runMain (module.js:575:10)
I tried installing body-parser, then lodash, and restarting my nodemon and mongod servers. It still won’t connect and threw a new error:
module.js:442
throw err;
^
Error: Cannot find module '/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/index.js’
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.runMain (module.js:575:10)
at run (bootstrap_node.js:352:7)
at startup (bootstrap_node.js:144:9)
at bootstrap_node.js:467:3
Any tips?
I Just reread the code and missed this VERY IMPORTANT LINE:
exports.delete_a_task = function(req, res) {
Edited my code and it worked! Thanks!
@victor, I’m glad its solved now :)
Sorry Olatunde, same error again, and I’m not sure what to do with this one. I was too quick to say it “worked”.
module.js:442
throw err;
^
Error: Cannot find module '/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/index.js’
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.runMain (module.js:575:10)
at run (bootstrap_node.js:352:7)
at startup (bootstrap_node.js:144:9)
at bootstrap_node.js:467:3
[nodemon] app crashed - waiting for file changes before starting…
Basically the error is here ‘/Users/victortran/code/personal_projects/hofb/hofb-product/productListApi/index.js’.
How can I be of help on this issue? send me your contact details.
Great tutorial, thanks!
Server Setup step 2 has an error:
port = process.env.PORT || 3000,
should be
port = process.env.PORT || 3000;
Cool…Thank you
Good tutorial…Thank you for it.
There is only one mistake in the text which took a couple of hours of time for me to figure out:
http://localhost:3000/task in POSTMAN is wrong and it should be http://localhost:3000/tasks
that small s would return an error of CANNOT GET/task
Thanks Aziz, typo corrected.
Thank You Bro
Hello
Before testing with postman, I tried to start the server with “npm run start”. But the app crashed, saying “MongoError: failed to connect to server”
Can you help and include the steps for using MongoDB in this tutorial?
Thank you
Hi,
You should install mongodb. On mac “homebrew” its most easy way, the command for install is “brew install mongodb” after you can start with command “sudo mongod”, after all this you can init the “npm run start”.
Hope help you.
Hi @ian_nie:disqus , The Error is because Mongodb server is not running. Kindly follow the steps as stated by @spanero .
Thanks for your suggestion, I’ll work towards that.