# Building a simple candlestick chart using Docker, FastAPI, and Vue 3 - Part 7

The next step in our journey is to hook up our frontend to the backend.
In the [last article](https://jeffpohlmeyer.com/candlestick-docker-fastapi-vue-part-6) we made things look a bit better and we set up some logic so that a loading spinner appears when we click on the `submit` button.
Now we need to use the `handleSubmit` method to actually fetch data from our server.

## Spin up the server
In case we don't recall from [Part 1](https://jeffpohlmeyer.com/candlestick-docker-fastapi-vue-part-1) of this series, our server is set up in a Python environment using `FastAPI`.
If our directory structure is like this
```
.
├── fast-api-vue-stock
│   ├── client
│   └── server
│       └── main.py
```
we can open a terminal/command prompt and type the following
```bash
cd /path/to/server
env\Scripts\activate
uvicorn main:app --reload
```
This assumes that we already have the same setup as was described in Part 1 of the tutorial.
By typing this then our server should be running at port 8000.

## Connecting to the server
### Create a querystring
For those who may not recall, the format of the url that we will be consuming in the backend is of the format `/quote/{ticker}/{interval}`.
Then, any date information, as it is optional, is passed in via query parameters in the format `YYYY-mm-dd`.
So, within the `handleSubmit` method in `Selections.vue` we first need to create a query object and add the start and end dates if they exist.
```javascript
const query = {}
if (!!state.startDate) query.start = state.startDate;
if (!!state.endDate) query.end = state.endDate;
```
Then, we can just map through the keys of the query and use the [encodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) built-in method to convert our dates into the correct format.
It should be noted that since we're only doing this with two values the method that will be presented below may be a little overkill, but the idea is that this method can be used for any number of query parameters so it's worth it to present.
```javascript
const queryString = Object.keys(query)
  .filter((key) => !!query[key])
  .map((key) => {
    return (
      `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`
    );
  })
  .join('&')
```
To see what this method does, let's use the following object as an example

```javascript
query = {
  "hello": "world",
  "hashnode": [1,2,3],
  "vue": {
    "is": "cool"
  },
  "fastapi": "is as well"
}
```
If we then look at the resulting string, we get
`'hello=world&hashnode=1%2C2%2C3&vue=%5Bobject%20Object%5D&fastapi=is%20as%20well'`
and we notice that there is an `object%20Object` where the value for the `vue` key should be.
This shows us that we can't just use this method for any deeply nested structures, but it instead works for simple key-value stores.
If we instead set 
```javascript
query = {
  "hello": "world",
  "hashnode": [1,2,3],
  "vue": ["is", "cool"],
  "fastapi": "is as well"
}
```
then the resulting string would look like `'hello=world&hashnode=1%2C2%2C3&vue=is%2Ccool&fastapi=is%20as%20well'` which would work much better.
There is an argument that you shouldn't need to do something like this when you're passing in the data yourself, but I've found that it can't hurt to just be extra careful with these sorts of things.
Now we should have a valid query string for our `start` and `end` dates, if they exist.

### Convert human-readable interval into yfinance style
The next step is to convert the interval into the style that `yfinance` needs.
For human readability, we're presenting our intervals as "Daily", "Weekly", and "Monthly", but these won't work in `yfinance` and we should actually get errors if we try to pass these values in because we've set the allowable input values to be one of `1d`, `1wk`, or `1mo`.
So, this is as simple as just using a nested JavaScript [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator).
```javascript
const interval = 
  state.interval === 'Daily'
    ? '1d'
    : state.interval === 'Weekly'
    ? '1wk'
    : '1mo';
```
This sets the `interval` constant to `1d` if the state's interval value is `Daily`, otherwise if the state's interval value is `Weekly` it sets it to `1wk`, otherwise it sets it to `1mo`.
The equivalent code in `if` statements would be
```javascript
let interval;

if (state.interval === 'Daily') {
  interval = '1d'
} else if (state.interval === 'Weekly') {
  interval = '1wk'
} else {
  interval = '1mo'
}
```

### Create the URL string and add the query if it exists
We can now create the URL that will be used to fetch the data from our server.
We first set up the basic url with the `symbol` and `interval` values
```javascript
let url = `http://localhost:8000/quote/${state.symbol}/${interval}`;
```
and we then need to append any query string, if it exists.
```javascript
if (queryString.length) url = `${url}?${queryString}`;
```
We do this because we need to put a `?` at the beginning of the query string.
The next step is to fetch the data from the server using the built-in `fetch` API.
```javascript
const response = await fetch(url);
const res = await response.json();
```

### Parse the dates and format data
#### Parse dates
The next step is to update the `state`'s `startDate` and `endDate` with the values obtained from the server.
This is done because one or both of the dates input by the user may have been weekends or holidays, or any other myriad possibilities.
We assume that the data we've received from the server contains valid dates, so the simplest way to do this is to just set `state.startDate` to the earliest date, and `state.endDate` to the latest date.
First, we want to create an anonymous function that will format the dates into the format we're currently using on the site.
```javascript
const formatDate = (d) => new Date(d).toISOString().substr(0, 10);
```
and then we sort the dates, and set each one
```javascript
const dates = res.data.map((e) => e.date).sort();
state.startDate = formatDate(dates[0])
state.endDate = formatDate(dates[dates.length - 1])
```

#### Format the data into our desired output
This last step is going to come slightly out of left field; we are going to format the data in a format that will eventually be used by our charting platform.
We're going to have the `x` coordinates be the dates and the `y` coordinates be the data, whether volume or stock prices.
```javascript
const data = res.data.map((e) => ({
  x: new Date(e.date),
  y: e.data
}));
const volData = res.data.map((e) => ({
  x: new Date(e.date),
  y: e.volume ?? 0
}));
```

The entire code of the `handleSubmit` method should now look like this
```javascript
const handleSubmit = async () => {
  v$.value.$validate();
  if (!v$.value.$invalid) {
    loading.value = true;

    // Create the querystring
    const query = {}
    if (!!state.startDate) query.start = state.startDate;
    if (!!state.endDate) query.end = state.endDate;

    const queryString = Object.keys(query)
      .filter((key) => !!query[key])
      .map((key) => {
        return (
          `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`
        );
      })
      .join('&')

    // Convert human-readable interval into yfinance style
    const interval =
      state.interval === 'Daily'
        ? '1d'
        : state.interval === 'Weekly'
          ? '1wk'
          : '1mo';

    // Create URL string and add query if it exists
    let url = `http://localhost:8000/quote/${state.symbol}/${interval}`;
    if (queryString.length) url = `${url}?${queryString}`;
    const response = await fetch(url);
    const res = await response.json();

    // Parse dates
    const formatDate = (d) => new Date(d).toISOString().substr(0, 10);
    const dates = res.data.map((e) => e.date).sort();
    state.startDate = formatDate(dates[0])
    state.endDate = formatDate(dates[dates.length - 1])

    // Format the data
    const data = res.data.map((e) => ({
      x: new Date(e.date),
      y: e.data
    }));
    const volData = res.data.map((e) => ({
      x: new Date(e.date),
      y: e.volume ?? 0
    }));
    
    loading.value = false;
  }
};
```
You'll notice that we removed the setTimeout and added an entry at the bottom to set the `loading` spinner to false.
Also, we needed to make the function itself `async` in order to utilize the `await` keyword.

In the [next article](https://jeffpohlmeyer.com/candlestick-docker-fastapi-vue-part-8) we'll discuss error handling, because having this type of functionality not wrapped in `try` and `catch` blocks is a recipe for disaster.
We'll also add in a dialog to alert the user when an error has taken place.
