awpala / node-2-afternoon

A node afternoon project to help solidify basic node concepts. Also covers full CRUD.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Project Summary

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.

Live Example

Click Me!

Setup

  • Fork this repository.
  • Clone your fork.

Step 1

Summary

In this step, we will create a package.json and install our dependencies.

Instructions

  • In the root of the project, create a package.json file.
  • Using npm, install and save express to the package.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.

Solution

Step 2

Summary

In this step, we will create a .gitignore file so our node_modules folder doesn't get tracked.

Instructions

  • Create a .gitignore file in the root of the project.
  • Add node_modules to the file.

Solution

.gitignore
node_modules

Step 3

Summary

In this step, we will create our index.js file.

Instructions

  • 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.

Solution

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}`);
});

Step 4

Summary

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.

Instructions

  • Create a controllers folder in server/.
  • Create a messages_controller in server/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 at 0 and increment after every creation.
  • Export an object with methods to create, read, update, and delete messages.
    • Create - Should be able to create a message using text and time off of the request body.
      • Should be able to assign a unique id to the message.
    • 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.
    • Delete - Should be able to delete a message using an id url parameter.
  • 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 ids 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.

Solution

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);
  }
};

Step 5

Summary

In this step, we will hook up our controller to our app in server/index.js.

Instructions

  • Open server/index.js.
  • Require the messages controller.
  • Create a post, get, put, and delete 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.
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:

Solution

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}`);
});

Step 6

Summary

In this step, we will test the API endpoints using Postman unit tests.

Instructions

  • Startup the API and make sure it doesn't crash.
  • Open Postman.
  • Import the postman_collection into Postman.
  • 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.

Solution

Step 7

Summary

In this step, we will setup the API to serve our front-end files.

Instructions

  • Open server/index.js.
  • Use express.static to serve the public/build folder.
    • Restart the API or Start the API.
  • Open http://localhost:3001/ to see the front-end interact with the API.

Solution

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}`);
});

Resources

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

Contributions

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.

Copyright

© 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.

About

A node afternoon project to help solidify basic node concepts. Also covers full CRUD.


Languages

Language:JavaScript 67.8%Language:HTML 16.5%Language:CSS 15.8%