In this project, we will create a local node chat server. After building out the server, we'll use postman unit tests to make sure the API has been built out correctly and also use a pre-built front-end to interact with the API.
Fork
this repository.Clone
yourfork
.
In this step, we will create a package.json
and install our dependencies.
- In the root of the project, create a
package.json
file. - Using npm, install and save
express
to thepackage.json
.
Detailed Instructions
Using npm
we can quickly create a package.json
file. In your terminal, when in the root of the project, run npm init -y
. The -y
flag will create a package.json
file with all the default values. It's that simple. Now we can use npm
to install packages and save them to the package.json
file. Run npm install express
to install and save the express package.
In this step, we will create a .gitignore
file so our node_modules
folder doesn't get tracked.
- Create a
.gitignore
file in the root of the project. - Add
node_modules
to the file.
.gitignore
node_modules
In this step, we will create our index.js
file.
- Open
server/index.js
. - Require
express
. - Create an express app.
- Configure the app to parse JSON from the body.
- Configure the app to listen on port 3001 and display a message when it is listening.
Detailed Instructions
To begin let's open server/index.js
and require the package our server will need. Express is a minimalist web framework for Node that will allow us to spin up a server in no-time. We can create an express application by requiring it and saving express()
to a variable. Let's create a variable called app
that equals express()
.
const express = require('express');
const app = express();
We also need to set up a body parser so that we have access to req.body
in our endpoints. The express package we imported has a method on it called .json
to do this. Set up an app.use(express.json())
app.use(express.json());
We now have a full express application stored in app
. If you were to console log app you would see it's a large object with many methods we can make use of. One of them we'll use is called listen
. This will allow us to say what port the server should listen on. Let's have our server listen on port 3001
.
const port = 3001;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
Why did you use a port variable? This variable is not required. However, say for some reason you needed to change the port, you now only have to change it in one place instead of two.
We now have an express server listening for requests on port 3001 and when we start up the server we'll see the console log of Server listening on port 3001
.
server/index.js
const express = require("express");
const app = express();
app.use(express.json());
const port = 3001;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
In this step, we will create a controller that will handle the logic to create, read, update, and delete messages. A message will be an object with an id
, text
, and time
property.
- Create a
controllers
folder inserver/
. - Create a
messages_controller
inserver/controllers/
. - Open
server/controllers/messages_controller.js
. - Create an array to hold the messages.
- Create a variable that will keep track of what
id
to assign to messages.- The
id
should start at0
and increment after every creation.
- The
- Export an object with methods to create, read, update, and delete messages.
- Create - Should be able to create a message using
text
andtime
off of the request body.- Should be able to assign a unique
id
to the message.
- Should be able to assign a unique
- Read - Should be able to return the messages array.
- Update - Should be able to update the
text
property of a message using the request body.- Should be able to determine which message to update using an
id
url parameter.
- Should be able to determine which message to update using an
- Delete - Should be able to delete a message using an
id
url parameter.
- Create - Should be able to create a message using
- All methods should send a response of the updated messages array.
Detailed Instructions
Now that we have a server listening for requests, let's create a controller that will execute logic when certain requests come in. Create a controllers folder in server/
and a messages_controller.js
file in server/controllers
. Inside that file let's create an array that will keep track of all the messages. We'll also need a variable that will keep track of what ID to assign to new messages to keep them unique. Let's create an id
variable that is equal to 0
.
let messages = [];
let id = 0;
Now let's use module.exports
to export an object. We'll put all our methods on this object. We are using module.exports
so that we can import the controller into index.js
to setup routes. I'll go into more detail when that time comes. For now, let's make a create
, read
, update
, and delete
method. Each method should be a function that has two parameters, one called req
and one called res
.
let messages = [];
let id = 0;
module.exports = {
create: (req, res) => {
},
read: (req, res) => {
},
update: (req, res) => {
},
delete: (req, res) => {
}
}
The create
method should create a new message object using text
and time
from the request body and also the global id
variable. It should then push this new messsage object into the messages
array. After a new message object is created, id
should be incremented by one so that the previous id
won't be used on any other future messages. This will effectively keep the id
unique for every message. We'll then want to send the updated messages
array.
create: (req, res) => {
const { text, time } = req.body;
messages.push({ id, text, time });
id++;
res.status(200).send(messages);
}
The read
method should return the entire messages array.
read: (req, res) => {
res.status(200).send(messages);
}
The update
method should update the text
property of a message using the text
value from the request body. It should also determine which message to update based on the value of id
from the request url parameters. We can use .findIndex
to get the index where the id
s match. We'll want to use double equals ==
to find the id
instead of triple equals ===
in this case because the id
in the message objects are numbers, and the id
from the req.params
is a string. We can then get the object using the index and update the object. Then we can return the updated messages
array.
update: (req, res) => {
const { text } = req.body;
const updateID = req.params.id;
const messageIndex = messages.findIndex(message => message.id == updateID);
let message = messages[messageIndex];
messages[messageIndex] = {
id: message.id,
text: text || message.text,
time: message.time
};
res.status(200).send(messages);
}
The delete
method should delete a message using the value of id
from the request url parameters. We can use .findIndex
again with the id
to get the index
of the message object and then use .splice
to remove it from the messages
array. We'll then want to send the updated messages
array.
delete: (req, res) => {
const deleteID = req.params.id;
const messageIndex = messages.findIndex(message => message.id == deleteID);
messages.splice(messageIndex, 1);
res.status(200).send(messages);
}
We now have all the logic we need to create
, read
, update
, and delete
messages. Now we can import our controller into index.js
and create endpoints that will execute the logic.
server/controllers/messages_controller.js
let messages = [];
let id = 0;
module.exports = {
create: (req, res) => {
const { text, time } = req.body;
messages.push({ id, text, time });
id++;
res.status(200).send(messages);
},
read: (req, res) => {
res.status(200).send(messages);
},
update: (req, res) => {
const { text } = req.body;
const updateID = req.params.id;
const messageIndex = messages.findIndex(message => message.id == updateID);
let message = messages[messageIndex];
messages[messageIndex] = {
id: message.id,
text: text || message.text,
time: message.time
};
res.status(200).send(messages);
},
delete: (req, res) => {
const deleteID = req.params.id;
messageIndex = messages.findIndex(message => message.id == deleteID);
messages.splice(messageIndex, 1);
res.status(200).send(messages);
}
};
In this step, we will hook up our controller to our app in server/index.js
.
- Open
server/index.js
. - Require the messages controller.
- Create a
post
,get
,put
, anddelete
endpoint that use the corressponding method on the messages controller. - The url for this api should be
/api/messages
.- Remember to add on a url parameter of
id
for the methods that are using it.
- Remember to add on a url parameter of
Detailed Instructions
Let's begin by opening server/index.js
. Since we used module.exports
in our server/controllers/messages_controller.js
we can require it in our index.js
. The entire index.js
will have access to all the methods we put on the object ( create
, read
, update
, and delete
).
const mc = require('./controllers/messages_controller');
We can then use the built-in methods express
gives us to create endpoints. We'll use post
for create
; get
for read
; put
for update
; and delete
for delete
. We'll also make a messagesBaseUrl
variable so that if the URL ever changes we won't have to update in four different places. The messagesBaseUrl
should equal /api/messages
.
const messagesBaseUrl = "/api/messages";
app.post(messagesBaseUrl, mc.create);
app.get(messagesBaseUrl, mc.read);
app.put(messagesBaseUrl, mc.update);
app.delete(messagesBaseUrl, mc.delete);
For the put
and delete
endpoints, we need to add on a url parameter of id
. A url paramter can be defined by adding :variableName
when making the URL for an endpoint.
const messagesBaseUrl = "/api/messages";
app.post(messagesBaseUrl, mc.create);
app.get(messagesBaseUrl, mc.read);
app.put(`${messagesBaseUrl}/:id`, mc.update);
app.delete(`${messagesBaseUrl}/:id`, mc.delete);
Now when a get
request is sent to http://localhost:3001/api/messages
our read
function will be executed in our messages_controller
. Which will then send a response of the messages array. Here is a map of what happens when certain requests come through:
- http://localhost:3001/api/messages ( POST ) -
create
frommessages_controller
executes - responds withmessages
array. - http://localhost:3001/api/messages ( GET ) -
read
frommessages_controller
executes - responds withmessages
array. - http://localhost:3001/api/messages ( PUT ) -
update
frommessages_controller
executes - responds withmessages
array. - http://localhost:3001/api/messages ( DELETE ) -
delete
frommessages_controller
executes - responds withmessages
array.
server/index.js
const express = require("express");
const mc = require("./controllers/messages_controller");
const app = express();
app.use(express.json());
const messagesBaseUrl = "/api/messages";
app.post(messagesBaseUrl, mc.create);
app.get(messagesBaseUrl, mc.read);
app.put(`${messagesBaseUrl}/:id`, mc.update);
app.delete(`${messagesBaseUrl}/:id`, mc.delete);
const port = 3001;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
In this step, we will test the API endpoints using Postman
unit tests.
- Startup the API and make sure it doesn't crash.
- Open
Postman
. - Import the
postman_collection
intoPostman
. - Run the collection's tests.
- If all tests do not pass, revist previous steps.
- TESTS WILL ONLY PASS IF THE 'MESSAGES' ARRAY IS EMPTY WHEN THE POSTMAN COLLECTION STARTS Restart your server (the command is 'rs') to reset your array to empty before running the tests.
In this step, we will setup the API to serve our front-end files.
- Open
server/index.js
. - Use
express.static
to serve thepublic/build
folder.- Restart the API or Start the API.
- Open
http://localhost:3001/
to see the front-end interact with the API.
server/index.js
const express = require("express");
const mc = require("./controllers/messages_controller");
const app = express();
app.use(express.json());
app.use(express.static(__dirname + '/../public/build'));
const messagesBaseUrl = "/api/messages";
app.post(messagesBaseUrl, mc.create);
app.get(messagesBaseUrl, mc.read);
app.put(`${messagesBaseUrl}/:id`, mc.update);
app.delete(`${messagesBaseUrl}/:id`, mc.delete);
const port = 3001;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
Express
- Req.Body Note: They use body-parser to parse the body, we use express.json
- Req.Query
- Req.Params
- Express.Static _Note: This will generally look like express.static(_dirname, '/path/to/build/folder')
- Get Request
- Post Request Note: Typically, you'll use post when you need to pass complex data (objects, arrays, etc) back to the server on the body post requests get access to req.body because of express.json and are used for adding new data
- Put Request Note: Typically, you'll use put when you need to pass complex data (objects, arrays, etc) back to the server on the body put requests get access to req.body because of express.json and are used for updating existing data
- Delete Request Note: Typically you'll use a path parameter to target what data you'd like to delete via an ID or other unique info
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.