---
# System prepended metadata

title: 08 Integrating Front End with Back End
tags: [BED, School, Notes, DISM]

---

---
title: '08 Integrating Front End with Back End'
disqus: hackmd
---

08 Integrating Front End with Back End
===

<style>
img{
/*     border: 2px solid red; */
    margin-left: auto;
    margin-right: auto;
    width: 90%;
    display: block;
}
</style>


## Table of Contents

[TOC]

### Front End
- how it'll work
    - user goes to valid url
    - client server send template html w/o data from db
    - js code bundled with html execute on user's browser
    - browser requests for data from our restapi with axios
    - once rest api responds with data, jquery used to append data to html
- jwt token will either be stored on localStorage or as a cookie

### CORS
- for security reasons browsers restrict cross-origin http reqs made by scripts
    - since front end server services file from localhost:3031 and API server is on localhost:3000, we are making cross-origin reqs
    - get around by adding `Access-Control-Allow-Origin` header in api resp with value `*` (hence allowing all)
    - install cors express middleware too
        - `npm install --save cors`
- [read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)


Practical Example
---
### Initialise
```javascript=
// index.js in root dir
app.get("/", (req, res) => {
  res.sendFile("/public/index.html", { root: __dirname });
});

app.get("/users/:id", (req, res) => {
  res.sendFile("/public/user.html", { root: __dirname });
});

app.get("/users/", (req, res) => {
  res.sendFile("/public/users.html", { root: __dirname });
});


const PORT = 3001;
app.listen(PORT, () => {
  console.log(`Client server has started listening on port ${PORT}`);
});
```

### Homepage
```htmlmixed=
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Friendbook</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
    <body>
    <div class="container">
        <nav class="nav">
            <a class="nav-link" href="/">Home</a>
            <a class="nav-link" href="/users/">All Users</a>
        </nav>
    
        <div style="margin-top: 2rem;">
            <h1>Home</h1>

            <form id="create-post-form" style="margin-top: 2rem;">
                <div class="form-group">
                    <textarea class="form-control" id="create-post-form-body" rows="3" placeholder="What's on your mind?"></textarea>
                </div>
                <button type="submit" class="btn btn-primary">Create Post</button>
            </form>

            <div id="posts">
            
            </div>
        </div>
    </div>

    <script
        src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // your JS code goes here.
        
    </script>
</body>
</html>
```

![](https://i.imgur.com/1tQXtjk.png)


### Fetching Posts
```javascript=
// append this js code into the script tag in html code above
// FETCHING POSTS
// API url
const baseUrl = "http://localhost:3000";
// hardcoded because we have not added login yet.
const loggedInUserID = 1;

axios.get(`${baseUrl}/users/${loggedInUserID}/posts/`)
.then((response) => {
    const posts = response.data;
    posts.forEach((post) => {
        const postHtml = `
        <div class="card" style="margin-top: 2rem;">
            <div class="card-body">
                <p class="card-text">${post.text_body}</p>
            </div>
            <div class="card-footer text-muted">
                ${post.created_at}
            </div>
        </div>
        `;

        $("#posts").append(postHtml);
    });
})
.catch((error) => {
    console.log(error);
});
```

### Implementing CORS in Controller
```javascript=
const express = require("express");
const app = express();

const User = require("./models/User");
const Post = require("./models/Post");

const bodyParser = require("body-parser");
app.use(bodyParser.json());

const cors = require("cors");
app.use(cors());
...
```

- now has required header
![](https://i.imgur.com/HWmuOCW.png)


### Building Profile Page
#### HTML file
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Friendbook</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <nav class="nav">
            <a class="nav-link" href="/">Home</a>
            <a class="nav-link" href="/users/">All Users</a>
        </nav>

        <div class="row" style="margin-top: 2rem;">
            <div class="col-md-8 col-xs-12">
                <div id="user-profile">
                </div>

                <div id="posts">
                </div>
            </div>

            <div class="col-md-4 col-xs-12">
                <h2>Friends</h2>
                <ul id="friends-list" class="item-group" style="list-style: none; padding-left: 0;">
                </ul>
            </div>
        </div>
    </div>
    <script
        src="https://code.jquery.com/jquery-3.3.1.min.js"
        integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
        crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        // JSCODE goes here
    
    </script>
</body>
</html>
```
#### JS Code
```javascript=
// js code
const baseUrl = "http://localhost:3000";
const loggedInUserID = 1;

const url = window.location.toString();
const userID = parseInt(url.split("/").slice(-1)[0]);
let posts;

axios.get(`${baseUrl}/users/${userID}`)
    .then((response) => {
        // append user profile
        const user = response.data;
        $("#user-profile").append(`
        <h1>${user.full_name}</h1>
        <p style="margin-top: 1rem;">${user.bio}</p>
        `);
    });

// fetch and append user posts
axios.get(`${baseUrl}/users/${userID}/posts/`)
    .then((response) => {
            posts = response.data;
            posts.forEach((post, index) => {
                const likeHtml = `
                <button class="btn btn-primary like-button" data-index=${index}>Like</button>
                `;

                const unlikeHtml = `
                <button class="btn btn-danger unlike-button" data-index=${index}>Unlike</button>
                `;

                const hasLiked = post.likers.map(liker => liker.id).includes(loggedInUserID);

                const postHtml = `
                <div class="card" style="margin-top: 2rem;">
                    <div class="card-body">
                        <p class="card-text">${post.text_body}</p>
                        ${ hasLiked ? unlikeHtml : likeHtml }
                        <h5 style="margin-top: 1rem;">Likers</h5>
                        <ul class="list-group">
                            ${
                                post.likers
                                    .map(liker => `
                                    <li class="list-group-item">
                                        <a href="/users/${liker.id}">${liker.full_name}</a>
                                    </li>
                                    `)
                                    .join("")
                            }
                        </ul>
                    </div>
                    <div class="card-footer text-muted">
                        ${post.created_at}
                    </div>
                </div>
                `;

                $("#posts").append(postHtml);
            });
        });

// fetch user's friends
axios.get(`${baseUrl}/users/${userID}/friends/`)
    .then((response) => {
        const friends = response.data;
        friends.forEach((friend) => {
            $("#friends-list").append(`
            <li class="list-group-item">
                <a href="/users/${friend.id}">${friend.full_name}<a/>
            </li>
            `);
        });
    });

// listens for like button clicks
// we have to use the .on method instead of $(".like-button").click because this
// supports listening to dynamically added like/unlike buttons.
// IMPORTANT: DO NOT USE arrow functions because then `this` will not refer to the button!
$(document).on("click", ".like-button", function() {
    const index = parseInt($(this).attr("data-index"));
    const post = posts[index];
    axios.post(`${baseUrl}/posts/${post.id}/likers/${loggedInUserID}`)
        .then((res) => {
            // change button to unlike on success
            const unlikeHtml = `
            <button class="btn btn-danger unlike-button" data-index=${index}>Unlike</button>
            `;
            $(this).replaceWith(unlikeHtml);
        });
});

// listens for unlike button clicks
$(document).on("click", ".unlike-button", function() {
    const index = parseInt($(this).attr("data-index"));
    const post = posts[index];

    axios.delete(`${baseUrl}/posts/${post.id}/likers/${loggedInUserID}`)
        .then((res) => {
            // change button to like on success
            const likeHtml = `
            <button class="btn btn-primary like-button" data-index=${index}>Like</button>
            `;
            $(this).replaceWith(likeHtml);
        });
});

```

### Login Page
#### HTML
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Friendbook</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <nav class="nav">
            <a class="nav-link" href="/">Home</a>
            <a class="nav-link" href="/users/">All Users</a>
        </nav>

        <h1>Login</h1>
        <p>Don't have an account?</p>
        <a href="/register/" class="btn btn-primary">Register</a>
        <form id="login-form">
            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" class="form-control" id="username">
            </div>
            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" class="form-control" id="password">
            </div>
            <button type="submit" class="btn btn-primary">Login</button>
        </form>
    </div>
    <script
    src="https://code.jquery.com/jquery-3.3.1.min.js"
    integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
    crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
    // js code goes here
    </script>
</body>
</html>
```

#### JS Code
```javascript=
const baseUrl = "http://localhost:3000";
$("#login-form").submit((event) => {
    // prevent page reload
    event.preventDefault();

    const username = $("#username").val();
    const password = $("#password").val();
    const requestBody = {
        username: username,
        password: password
    };

    axios.post(`${baseUrl}/login/`, requestBody)
        .then((response) => {
            const token = response.data.token;
            const loggedInUserID = response.data.user_id;
            localStorage.setItem("token", token);
            localStorage.setItem("loggedInUserID", loggedInUserID);
            window.location.href = "/";
        })
        .catch((error) => {
            console.log(error);
        });
});
```

### Redirect if not logged in
- redirect if no token in localStorage

#### JS Code
```javascript=
// edit in script tag of index and user.html
const baseUrl = "http://localhost:3000";
const token = localStorage.getItem("token");
const loggedInUserID = parseInt(localStorage.getItem("loggedInUserID"));

if (token === null || isNaN(loggedInUserID)) {
    window.location.href = "/login/";
} else {
    // put your original code in the script tag here
}
```




###### tags: `BED` `DISM` `School` `Notes`