September 24th, 2017
Express is a Node Web Framework that was created to build Web apps and APIs. In this tutorial, we’re going to look at how to create a todo API using express. Now what is a REST API? REST stands for (Representational State Transfer), while API stands for (Application Programming Interface). Basically a REST API allows developers to make HTTP requests to it and in return receive a response.
This project will be nice and small. Here’s a look at the base files and folders.
1todo-api
2|--models
3|----todo.js
4|--node_modules
5|--routes
6|----todos.js
7|--package.json
8|--server.js
Start up MongoDB
Create a directory anywhere in your computer and change directories into it.
Open a up a terminal and run npm init and hit enter through the prompts.
Install nodemon globally. npm install -g nodemon. This will refresh changes to our project.
Install dependencies
1npm install -S express mongoose body-parser morgan cors
or if you prefer Yarn:
1yarn add express mongoose body-parser morgan cors
Create a file called server.js in your project. This file will essentially have all of our server logic hence the name server.js.
1'use strict';
2
3/* Packages */
4var express = require('express');
5var bodyParser = require('body-parser');
6var logger = require('morgan');
7var mongoose = require('mongoose');
8var cors = require('cors');
9var port = process.env.PORT || 8080;
10var app = express();
11
12/* Connect to Mongo */
13mongoose.connect("mongodb://localhost:27017/todos");
14var db = mongoose.connection;
15db.on("error", function (err) {
16 console.error("\x1b[31m%s\x1b[0m", "Connection error:", err);
17});
18db.once("open", function () {
19 console.log("\x1b[36m%s\x1b[0m", "Connected to Mongo");
20});
21
22/* Middleware */
23app.use(logger("dev")); // logs http requests in console
24app.use(bodyParser.json()); // enables us to get data from a post request
25
26// catch 404 and forward to error handler
27app.use(function (req, res, next) {
28 var err = new Error("Not Found");
29 err.status = 404;
30 next(err);
31});
32
33// Error handler
34app.use(function (err, req, res, next) {
35 res.status(err.status || 500);
36 res.json({
37 error: {
38 message: err.message
39 }
40 });
41});
42
43// Start Server
44app.listen(port, function () {
45 console.log("\x1b[36m%s\x1b[0m", "Server up and running on port:", port);
46});
Just with any node project, we require our dependencies that we installed earlier at the top of the file. Next we connect to our local MongoDB service and append the name of the database we want to create which is /todos.
Now, in your terminal run: nodemon server.js. Make sure you are running MongoDB in the background or else you might receive a console error in your terminal. If everything went smoothly, you should see:
1Server up and running on port: 8080
2Connected to Mongo
at the end of your console.
Notice the app.use() methods in the file. These are express’ middleware. They handle events and routes when triggered. Later we will add our todos routes middleware into the mix so we can create, read, update and delete todos.
Lastly we have the app.listen() method, this basically starts our server at a given port. We set it to the node environment’s port or port 8080. You can visit localhost:8080 in your browser, you should see an error message saying not found. We have not set any routes in the root of the server, that’s why it defaulted to use the 404 middleware.
In your project directory create a folder called Models and then a file called Todo.js within it. This file will shape how we want our todos to look. We want a title, status and creation_date for every todo. We’ll use the mongoose package to help us achieve this.
1'use strict';
2
3var mongoose = require("mongoose");
4var Schema = mongoose.Schema;
5
6var TodoSchema = new Schema({
7 title: {
8 type: String,
9 required: true,
10 trim: true
11 },
12 status: {
13 type: Boolean,
14 required: true,
15 default: false
16 },
17 creation_date: {
18 type: Date,
19 required: true,
20 default: Date.now
21 }
22});
23
24var Todo = mongoose.model('Todo', TodoSchema);
25module.exports = Todo; // export `Todo` variable to be available for other files
Now is the time to create our routes and test our endpoints. We’ll be using Express’ router to help us. When we send a GET request to localhost:8080/api/todos, our goal is to get a list of all the todos from the todos database in json form. When we send a POST request to the same endpoint, we want to create a new todo from a parsed json object. Here’s a table to better illustrate what we’re trying to accomplish.
Create a routes folder, then create a file named Todos.js inside.
1"use strict";
2
3var express = require("express");
4var router = express.Router();
5var mongoose = require('mongoose');
6
7var Todo = require("../models/todo");
8
9router.get('/', function(req, res, next) {
10 res.json({message: 'API working!'})
11});
12
13module.exports = router; // export `router` variable to be available for other files
Import the routes/todos.js file near top of server.js file.
1var todosRoute = require('./routes/todos.js');
Add a todos route middleware at /api/todos just above the 404 error handler.
1// Register 'todos' Route
2app.use("/api/todos", todosRoute);
Notice how we’re using /api/todos in the route middleware and then passing the todosRoute that we imported. All the routes that we define in the Routes/todos.js will be based on /api/todos. So routes referencing "/" in Routes/todos.js will go to /api/todos (localhost:8080/api/todos).
Open up postman. Postman will help us test HTTP verbs that aren’t supported by browsers by default. Now in Postman’s url bar go to localhost:8080/api/todos, make sure the HTTP verb is set to GET and press send. You should see something like this.
Let’s actually get some real todo data up in here. First let’s create a post method.
1router.post('/', function(req, res, next) {
2 var todo = new Todo(req.body);
3 todo.save(function(err, todo) {
4 if (err) return next(err);
5 res.status(201);
6 res.json({message: "Todo Created!", todo: todo});
7 });
8});
Now let’s add our first todo! Change the HTTP Verb from GET to POST. Click the body tab, select the raw radio button and select JSON(application/json) from the dropdown. Let’s set Mow the lawn as our first todo. Since our Todo Model has default fields for the creation date and status, we only need to supply the title field. In the textarea below the radio buttons enter a json object for the todo like so.
Add another todo, Get a haircut just for kicks.
Let’s change our get all todos method to retrieve all of our todos.
1router.get('/', function(req, res, next) {
2 Todo.find(function(err, todos){
3 if (err) return next(err);
4 res.json(todos);
5 });
6});
Test with postman, change HTTP verb back to GET, make sure url is localhost:8080/api/todos and press send. We should get back two total todos: Mow the lawn and Get a haircut.
We have three more actions to finish. All three will use the id params. Thankfully, Mongo automatically creates an id field for each todo document of type ObjectId.
1router.get('/:id', function(req, res, next) {
2 var id = mongoose.Types.ObjectId(req.params.id); // cast id param to ObjectId
3 Todo.findById(id, function (err, todo) {
4 if (err) return next(err);
5 if (!todo) {
6 err = new Error("Todo Not Found");
7 err.status = 404;
8 return next(err);
9 } // if todo doesn't exist, set err
10 res.json(todo);
11 });
12});
In Postman, copy the id value for Mow the lawn and append it to the url to check.
We’ll use the PUT method to update our todos. Within that we’ll use the findByIdAndUpdate mongoose method. This will allow us to return the new todo in the response with the use of the { new: true } parameter. We’ll also set the validators to true so users can’t enter a null todo.
1router.put('/:id', function (req, res, next) {
2 Todo.findByIdAndUpdate(
3 req.params.id,
4 req.body,
5 {
6 new: true,
7 runValidators: true
8 }, function (err, todo) {
9 if (err) return next(err);
10 if (!todo) {
11 err = new Error("Todo Not Found");
12 err.status = 404;
13 return next(err);
14 }
15 }
16 res.json({ message: "Todo updated!", todo: todo });
17 });
18});
Change the HTTP verb in Postman to PUT. Make sure url is at localhost:8080/api/todos/[id-of-mow-the-lawn]. Now just like the post method we’re going to go the body tab, select raw and select JSON(application/json) from the dropdown. Let’s update Mow the lawn’s status to true, meaning we’ve completed the todo. In the textarea, type a json object with status equaling true.
1{
2 "status": true
3}
Finally, for our last HTTP operation, we’ll set up a delete route. We can use mongoose’s remove method to achieve deleting a todo from the database.
1router.delete('/:id', function (req, res, next) {
2 Todo.findById(req.params.id, function (err, todo) {
3 if (err) return next(err);
4 if (!todo) {
5 err = new Error("Todo Not Found");
6 err.status = 404;
7 return next(err);
8 }
9 todo.remove(function(err) {
10 if (err) return next(err);
11 res.json({message: "Todo deleted!"});
12 })
13 });
14});
And just like that, we have a RESTful API up and running. In order for other domains to interact and fetch data from this API we will have to enable the cors package. CORS stands for Cross-Origin Resource Sharing. Domains, by default don’t allow other domains to fetch their data, we can remedy this by enabling cors to specific routes or all routes. We will tackle this in a future blog post with React.js.