---
# System prepended metadata

title: 'CS 35L: Connecting Your Project'

---

# CS 35L: Connecting Your Project

Written by Benson Liu (CS 35L S24 LA).

One of the most critical parts of the project you probably are leaving with your group to the last minute is connecting your frontend and backend together. This is actually one of the **hardest** parts of your first software construction project; especially, when trying to deal with user **authentication** and **cookies**. This is meant as sort of a quick one page document to crash course you into figuring out and debugging some of the problems that you might run into. But also, **don't leave this to last minute**. ;-;

## Web Communcation Fundamentals

For this document, I will be explainining how application communication works in terms of **REST APIs** and using the browser's [**Fetch API**](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). There are technically a few ways to do this but this is one of the more widely used ways to do it. Your group is free to pursue alternative solutions such as using axios and other non-REST forms of APIs. I will be making some assumptions while explaining this document such as your group is working using the **MERN** stack where your React frontend is running on http://localhost:3000 and your Express.js backend is running on http://localhost:5000.

### REST APIs

RESTful or REST Application Programming Interfaces (APIs) are meant as a way to communicate between clients and servers in full-stack applications using HTTP. There are actually a couple different ways to communicate in networked applications (e.g. gRPC) but REST is one of the most popular and the one we talk about most for this class. REST breaks down communication between **endpoints** which are specific parts of your server where you separate functionality (e.g. `/api/user` to handle user creation, deletion, etc). The way you can distinguish between different actions to a particular endpoint is by using the HTTP request **method**. There are technically quite a few methods but these are the most commonly used.

- OPTIONS: used to see which HTTP request methods can be used on an endpoint
- GET: retrieving data from and endpoint (what your browser and fetch does by default)
- POST: often used when trying to **create** data
- PUT: used when trying to **update** data
- DELETE: used when trying to **remove** data

Often times data sent over REST APIs include a request body (note: GET and OPTIONS do not include a request body but the others do) which is commonly in the form of JavaScript Object Notation (**JSON**). An example JSON is shown below.

```javascript=
{
    "username": "admin",
    "password": "password",
    "extra": [1, 2, 3, 4, 5],
    "cool": {
        "a" : 1,
        2: "z",
    }
}
```

![image](https://hackmd.io/_uploads/rkAs9Bt3a.png)

### Fetch API

The fetch API is a builtin JavaScript API in the browser that allows you to make HTTP requests. Another popular library that is used is `axios` but for the sake of this document, we will use fetch instead (mostly because I am more familiar with it but also because it is native to the browser). An example request is shown below.

```javascript=
fetch("https://localhost:3000");
```

By default, fetch makes a get request but if you try running this in the console, you may see something confusing about **Promises**. fetch is an example of a asynchronous function in JavaScript. These are meant to allow for other parts of the code to execute while the rest is executing. This is common in network requests because these can often take a long period of time so a Promise is returned and is resolved once the request is completed or failed. However, in some cases, you care about the specific response that is returned. An example of making a POST request is shown below.

```javascript=
let res = await fetch('http://localhost:5000/api/user', {
    method: 'POST',
    headers: {
        'content-type': 'application/json'
    },
    body: JSON.stringify({
        user: 'admin',
        pass: 'password123'
    }),
});

let json = await res.json();
console.log(json);
```

A quick note is that if you are writing functions and need to use async/await, you need to include this inside other async functions. An example of this is shown below. You may need to be careful when doing this such as including these inside a `useEffect` as there are some [quirks](https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret) with this.

```javascript=
async function createUser(username, password) {
    let res = await fetch('http://localhost:5000/api/user', {
        method: 'POST',
        headers: {
            'content-type': 'application/json'
        },
        body: JSON.stringify({
            user: 'admin',
            pass: 'password123'
        }),
    });
    if(await res.status !== 200) {
        alert("Creating a user failed!");
        return;
    }

    let json = await res.json();
    console.log(json);
}
```

## Why is connecting things hard?

When first trying to connect your frontend to your backend, you may run into this error.

![image](https://hackmd.io/_uploads/S1OKrzO36.png)

What exactly is this error? Why is it occuring? To get a better sense of why this occurs we need to understand some of the basics of **web security**.

### Same-Origin Policy

The Same-Origin Policy is a security isolation technique implemented in browsers to separate different websites' resources from each other. The big idea behind this is that sensitive resources such as cookies and content cannot be accessed **cross-origin**. This is great defense since it ensures that if an adversaries opens up a tab in one one window, that they would not be able to access the resources of a different website open in another window. An **origin** is composed of three different parts: **scheme/protocol** (e.g. https), **host/domain name** (e.g. example.com), and the port (e.g. 443). A quick note is that there are a couple implied ports such as port 80 when visiting a website over HTTP and 443 when visiting a website over HTTPS.

![image](https://hackmd.io/_uploads/Hk_J8GOn6.png)

The Same-Origin Policy can be thought of as a simple if-statement where if the origin of where a JavaScript request is coming from does not match the origin of where it is trying to access (e.g. via a fetch request). Then the browser would not execute this request at all (even if one part is different such as the port number).

![image](https://hackmd.io/_uploads/BkCQLGOha.png)


The **Same-Origin Policy** is a big problem for your CS 35L final project because you are making requests **cross-origin**. This is because if we compare the origin of your frontend React app (i.e. http://localhost:3000) and backend Express.js app (i.e. http://localhost:5000) these are **different origins** (the reason is because the **port numbers** are **different**). This is a big problem since we not only need to figure out how to make requests cross-origin but also how to send **sensitive information** like cookies. How can we do this?

### Cross-Origin Resource Sharing (CORS)

A way to allow cross-origin requests is to whitelist certain origins from the server. **Cross-Origin Resource Sharing (CORS)** are rule set by the server but enforced by the browser. If a browser is about to make a reques cross-origin, it first sends a **preflight OPTIONS** request to figure out of there are any special rules that allow it to make the request cross origin. If there, are then it finally makes the request itself.

![image](https://hackmd.io/_uploads/BJy5wrKha.png)

There are a few special headers that allow cross-origin requests to be made. An example of the following that would sufficient for your project is shown in the example HTTP response below.

```
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: 'true'
Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE
...
<rest of HTTP response>
```

`Access-Control-Allow-Origin` specifies the specific origin which is allowed to make cross-origin requests to this server. `Access-Control-Allow-Credentials` determines whether or not the browser is allowed to send cookies in the request. `Access-Controlw-Allow-Methods` specifies the specific type of HTTP request methods that are allowed to be made to this specific origin. In this case, you need all parts to make sure that your application is working smoothly in this specific context. Most modern backend frameworks like Express.js + Flask allow you to manually set the headers.

Note that an easy mistake is to try and set the header, `Access-Control-Allow-Origin: *`. Browsers do not send cookies to origins that have the `Access-Control-Allow-Origin` header set to `*` for security reasons. You will need to specify the specific origin of your frontend on your backend server.

But wait, isn't this a lot to remember? 😅 Luckily, all major backend frameworks implement a library that tries to abstract CORS away to make your life easier. If you are using Express.js, you can check out the [CORS middleware](https://expressjs.com/en/resources/middleware/cors.html) to easily integrate this into your project. Some students have had some trouble using this framework in the past. Feel free to come to office hours if you need some help. You could also implement the above mentioned headers manually as this is something that every CORS library does under the hood.

## How do we code this?

An example of using the fetch API is shown below to implement this. As you can see, we are able to use `credentials: 'include'` to send cookies. The part of this that indicates that we are allowed to send the request cross-origin is the special HTTP headers sent by the server.

```javascript=
fetch('http://localhost:5000/api/user', {
    method: 'POST',
    headers: {
        'content-type': 'application/json'
    },
    credentials: 'include',
    body: JSON.stringify({
        user: 'admin",
        pass: "password123'
    }),
});
```

A quick note about a solution that **does not** work. Examine the following code snippet bellow.

```javascript=
fetch('http://localhost:5000/api/user', {
    method: 'POST',
    headers: {
        'content-type': 'application/json'
    },
    credentials: 'include',
    mode: 'no-cors',
    body: JSON.stringify({
        user: 'admin',
        pass: 'password123'
    }),
});
```

Some folks may find some misleading posts online about using `mode: 'no-cors'` in order to bypass the Same-Origin Policy or CORS errors. The reason why this **does not** work is because `no-cors` **does not** send cookies and ignores the `credentials: 'include'` and defeats the whole point of trying to make these cross-origin requests for authentication.

## Is everything completely "fixed"?

Short answer is **no**. But this is enough information in order for you to be able to complete your CS 35L project. 🫠

Technically, it may be possible to avoid some of this while tackling your project by using a very hacky solution of sending your authentication tokens through the body of your request and avoid using cookies all together. If your group finds this easier, that should be okay for this project. However, this is the more precise solution.

If you are interested in **deploying** your app, you will need to speak to a TA or LA during office hours because you may need to configure **SameSite** cookies or **TLS**. If web security sounds cool to you, feel free to come talk to Benson during office hours. 🤗