Per-User Feeds Tutorial

In a previous tutorial we created a comment wall that any user on the page can view and publish to. What if we want to create a comment wall per user that only they can publish to? This is what we are going to implement:

If you missed the previous tutorial, or wish to view the completed code for this part, you can download it all from the GitHub repo.

Creating a Login Form

The first step is to implement a basic login form and sessions for our users. Let's replace the existing static/index.html with a login form. Save the original index page; we will use it later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE HTML>

<html>
<head>
  <title>Feeds comments wall</title>
  <link
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
    rel="stylesheet">
</head>

<body style="padding: 30px;">
  <form action="/login" method="post" style="border-style: solid; width: 50%;
      margin: auto; padding: 10px;">
    <label style="width: 14%;">username</label>
    <input name="user_id" type="text" style="width: 69%;">
    <input type="submit" value="log in" style="width: 14%;">
  </form>
</body>
</html>

For the sake of this tutorial, we won't require passwords.

Setup Server Sessions

On the server we need to installexpress-session:

1
$ npm install --save express-session

And require it:

1
const session = require('express-session')

Then enable it in index.js. We also need to use the urlencoded body parser because that is how POST data from the login form is submitted.

1
2
app.use(session({ secret: 'SOME-SECRET' })); // Any secret will do
app.use(bodyParser.urlencoded({ extended: false }));

Finally we need to handle requests to login, which should start a session and redirect the user to the comments wall page.

1
2
3
4
app.post("/login", (req, res) => {
  req.session.userId = req.body.user_id;
  res.redirect(`/comments/${req.body.user_id}`);
});

For now the sessions will just be in-memory, so will not persist across server restarts.

Subscribing to Per-User Feeds

We now need to let the logged-in user publish and subscribe to a their personal feed. We can do this by using a feed ID that is the same as whatever username they are logged in with. In practice you should use something like a user ID because user-entered data will not always conform to a valid feed ID (alpha-numeric plus dashes).

The problem is that subscriptions happen on the client, and currently we do not have access to the username there. To solve this will use a templating engine (ejs) to embed the username in the html file that is sent to the client. You can use a different templating engine here if you are more familiar with it.

First we need to install ejs:

1
$ npm install ejs --save

And enable it in index.js by adding:

1
2
app.set('views', './views');
app.set('view engine', 'ejs');

Then rename the original static/index.html to views/comments.ejs, and add the following code to index.js in order to render and serve the template:

1
2
3
app.get("/comments/:user_id", (req, res) => {
  res.render('comments', { userId: req.params.user_id });
});

In views/comments.ejs where the feed is instantiated, you can update the feed ID to the template placeholder userId:

1
2
3
const commentsFeed = new Feeds({
  instanceLocator: "YOUR_INSTANCE_LOCATOR_HERE"
}).feed("<%= userId %>");

Publishing to Per-User Feeds

The final step is to support publishing to these per-user feeds.

On the client we need to update the code that submits the comment to also send the username of the poster. We can do this by adding to the URL the POSTs are made to:

1
xhr.open("POST", "/comments/<%= userId %>", true);

On the server, update the new comment handler so that it publishes comments to the feed with the same ID as the user ID. We also need to check that the user ID in the request matches the user ID in the session, otherwise logged in users could manually enter the URL of a different user's comments wall, and publish comments on their wall!

1
2
3
4
5
6
7
8
9
10
app.post("/comments/:user_id", (req, res) => {
  if (req.session.userId === req.params.user_id) {
    feeds
      .publish(req.params.user_id, req.body)
      .then(() => res.sendStatus(204))
      .catch(err => res.status(400).send(err));
  } else {
    res.sendStatus(401);
  }
});

You have now finished implementing per-user feeds!

There is a flaw however: at the moment, a logged-in user can still view the comments of another user if they visit their comments wall.

Even if you attempted to block this with client-side JavaScript, there is nothing stopping a malicious user manually subscribing to any feed they wish. However there is a solution: private feeds. In part 3 of the tutorial we will see how use these.

Did you find this document useful?

We are always striving to create the most accurate and informative docs as possible. If there is something especially wrong (or right) here then please let us know.