In this project, we will be creating a chat application that will use sessions and custom middleware.
- Fork and clone this repository.
cdinto the project.- Run
npm install.
In this step, we'll use npm to install express and dotenv and set up our server file.
- Run
npm install express dotenv. - Create a folder called
server. Within theserverfolder, create theindex.jsfile. - Create a
.envfile in the root folder of your project. Add the propertySERVER_PORTto your.envfile and assign it the value of3005 - Set up your server file using
express,express.jsonmiddleware, and listening on a port.- Don't forget to configure
dotenvto use with your session secret and include your.envfile in your.gitignore.
- Don't forget to configure
server/index.js
require("dotenv").config();
const express = require("express");
let { SERVER_PORT } = process.env;
const app = express();
app.use(express.json());
app.listen(SERVER_PORT, () => {
console.log(`Server listening on port ${SERVER_PORT}.`);
}); .env
SERVER_PORT = 3005
In this step, we will set up the proxy so that all non text/html requests are forwarded on to our node/express server. We'll also set up the main property so you can easily start your server with nodemon.
- Open your
package.jsonfile. - Add the following line.
"proxy": "http://localhost:3005"
- Add the following line.
"main": "server/index.js"
package.json
...
"main": "./server/index.js",
"proxy": "http://localhost:3005"
}In this step, we will set up needed endpoints and create a controller file.
- Create a file called
messagesCtrl.jsin theserverfolder. - Create a variable called
allMessages, which is an empty array. - Export an object using
module.exports.- Add a method called
getAllMessagesthat responds with theallMessagesvariable.
- Add a method called
- Create a GET endpoint with a path of
/api/messagesand use thegetAllMessagesmethod as the callback.- Don't forget to require the controller file.
- Add another method to the controller file called
createMessage.- A username and message will be sent in the body of the request. Create a new message object with the
usernameandmessageproperties. Push the new message object into theallMessagesarray. - Respond with the updated
allMessagesarray.
- A username and message will be sent in the body of the request. Create a new message object with the
- Create a POST endpoint with a path of
/api/messageand use thecreateMessagemethod as the callback.
index.js
require("dotenv").config();
const express = require("express");
const messagesCtrl = require("./messagesCtrl");
let { SERVER_PORT } = process.env;
const app = express();
app.use(express.json());
app.get("/api/messages", messagesCtrl.getAllMessages);
app.post("/api/message", messagesCtrl.createMessage);
app.listen(SERVER_PORT, () => {
console.log(`Server listening on port ${SERVER_PORT}.`);
}); messagesCtrl.js
let allMessages = [];
module.exports = {
getAllMessages: (req, res) => {
res.status(200).send(allMessages);
},
createMessage: (req, res) => {
const { username, message } = req.body;
let newMessage = {
username,
message
};
allMessages.push(newMessage);
res.status(200).send(allMessages);
}
};In this step, we will start making HTTP requests, from our react app to our node/express server, so that we can
- get all messages and display them
- create a new message
- Open
App.jsand importaxiosat the top of the file. - Add the
componentDidMountlifecycle method and make a GET request to your node/express api- path:
'/api/messages' - Set state with the response. Update the
allMessagesproperty on state.
- path:
- Find the
createMessagemethod and make a post request. Sendthis.state.usernameandthis.state.messagein the body of the request. Useusernameandmessageproperty names.- path:
'/api/message' - body:
{username: this.state.username, message: this.state.message}
- path:
- Set state with the response (which will be the updated array messages from the server)
- Update the
allMessagesproperty.
- Update the
App.js
...
componentDidMount() {
axios.get('/api/messages').then(res => {
this.setState({ allMessages: res.data });
});
}
createMessage() {
axios
.post("/api/message", {
username: this.state.username,
message: this.state.message
})
.then(res => {
this.setState({
allMessages: res.data
});
});
}
...In this step, we will set up sessions using the express-session library. By using sessions, we will be able to keep track of a message history for each user on our app.
At this point, you should have a working app where you can save your username and send messages.
- Run
npm i express-session; - This library is middleware. We need to configure sessions using the built-in express method
app.use(); - At the top of
index.js, require in the libraryconst session = require('express-session');- In the
.envfile, add a property calledSESSION_SECRETwith an associated value. In theindex.jsfile, destructure this value from theprocess.envobject.
- Configure this top level middleware like this:
app.use(session({ secret: SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { maxAge: 1000 * 60 * 60 } }))
- secret: The session secret will add a basic layer of security.
- resave: Forces the session to be saved back to the session store, even if the session was never modified during the request (info from docs).
- saveUninitialized: Forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified (info from docs).
- cookie: Allows us to customize the seesion cookie. Here we are setting the maximum age of the cookie to 1 hour (1000 milliseconds in a second, 60 seconds in a minutes, 60 minutes in one hour)
index.js
require("dotenv").config();
const express = require("express");
const messagesCtrl = require("./messagesCtrl");
const session = require("express-session");
let { SERVER_PORT, SESSION_SECRET } = process.env;
const app = express();
app.use(express.json());
app.use(
session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: false
})
);
....env
SERVER_PORT = 3005
SESSION_SECRET = jfdfjkdslajfdsksuperdupersecretfdjskalfjdsaweifj
In this step, we will use sessions to create a message history.
-
In the
messagesCtrl.jsfile, find thecreateMessagemethod. We are currently taking all messages that are sent to the server and storing them in theallMessagesarray. In addition to this, we will use the user's current session to store all the messages from the user. -
To access session data, just use the property
req.session.req.sessionis an object that we can use to store whatever we want. In this case, we want to add a property calledhistorythat will be an array.- NOTE: Remember, we have access to the
req.sessionobject because we are using theexpress-sessionlibrary.
- NOTE: Remember, we have access to the
-
We need to initialize the
req.session.historyproperty if it doesn't already exists on thereq.sessionobject. Then push the new message object into thereq.session.historyarray.if (req.session.history) { req.session.history.push(newMessage); } else { req.session.history = []; req.session.history.push(newMessage); }
messagesCtrl.js
let allMessages = [];
module.exports = {
getAllMessages: (req, res) => {
res.status(200).send(allMessages);
},
createMessage: (req, res) => {
const { username, message } = req.body;
let newMessage = {
username,
message
};
allMessages.push(newMessage);
if (req.session.history) {
req.session.history.push(newMessage);
} else {
req.session.history = [];
req.session.history.push(newMessage);
}
res.status(200).send(allMessages);
}
};In this step, we will display the user's message history in history modal.
- In the
HistoryModal.jsfile, importaxiosand find thecomponentDidMountmethod.- Make a GET request to fetch the message history for the user.
- path:
'/api/messages/history' - Update the
historyMessagesproperty with the response.
- path:
- Make a GET request to fetch the message history for the user.
- Since we don't have an endpoint for the above request, let's go create one.
- In the
index.jsfile, add a GET endpoint with a path of'/api/messages/history'. - Add a method named
historyto the messages controller.historyshould return all the messages stored on the session.
- In the
HistoryModal.js
import React, { Component } from 'react';
import axios from 'axios';
import './HistoryModal.css';
export default class HistoryModal extends Component {
constructor(props) {
super(props);
this.state = {
historyMessages: []
};
}
componentDidMount() {
axios.get("/api/messages/history").then(res => {
this.setState({
historyMessages: res.data
});
});
}
... index.js
...
app.get("/api/messages", messagesCtrl.getAllMessages);
app.get("/api/messages/history", messagesCtrl.history);
app.post("/api/message", messagesCtrl.createMessage);
app.listen(SERVER_PORT, () => {
console.log(`Server listening on port ${SERVER_PORT}.`);
}); messagesCtrl.js
...
history: (req, res) => {
res.status(200).send(req.session.history);
}
};In this step, we will add in custom middleware. Sometimes you just cannot trust some of the potty mouth people out there in the world. We are going to create middleware that will filter out bad words from our users' messages.
-
In
index.js, add in some top level, custom middleware. If there is amessageproperty on the body, write some filter logic to make sure that bad words are removed. Below is an example, but there are different ways to accomplish this.- Note: the example below is using a regular expression. Regular expressions are patterns used to match character combinations in strings. The regular expression below is searching for our bad words using the 'g' flag, which searches the string gloabally for all instances of our bad words...then replaces them with '****'.
app.use((req, res, next) => {
let badWords = ['knucklehead', 'jerk', 'internet explorer'];
if (req.body.message) {
for (let i = 0; i < badWords.length; i++) {
let regex = new RegExp(badWords[i], 'g');
req.body.message = req.body.message.replace(regex, '****');
}
next();
} else {
next();
}
});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, 2019. 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.
