1

I'm currently stuck with a problem in a homework project. I'm trying to make a project where the price of bitcoin will update every second. Now the API request is working fine and I can see the data render from an EJS template but I can't make the price update every second. Can anyone check my code and see if anything is wrong in my code? For reference please check www.preev.com. It shows how I want the price to be updated. And also check below my code.

I have tried to call the API request in app.js file and rendered it in an EJS template called results.ejs.

app.js

var express = require("express");
var app = express();
var request = require("request");


app.set("view engine", "ejs");


app.get("/", function(req, res) {
    request("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true", function(error, response, body) {
        if(!error && response.statusCode == 200) {
            var data = JSON.parse(body);
            res.render("result", {data: data});
        }
    });
});




app.listen(3000, function(){
    console.log("server has started");
});


results.ejs

<h1>
    Bitcoin Latest
</h1>



Price: $ <span id="showPrice"></span> 
<br>
MarketCap: $<%= data["bitcoin"]["usd_market_cap"] %>
<br>
24h Volume: $<%= data["bitcoin"]["usd_24h_vol"] %>
<br>
24h Change: <%= data["bitcoin"]["usd_24h_change"] %>%


<script>
    function updatePrice(){
        document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;
    };

    setInterval(updatePrice, 500);
</script>
Zak
  • 860
  • 16
  • 39
  • 1
    That ejs document is processed on the server when the user visits the page, which means "ejs code" like `<%= data["bitcoin"]["usd"] %>` runs exactly once, then never again until the page is refreshed. You need to create a second route in your express app that sends back only the usd value, then request that from your script using `fetch()`. –  Sep 01 '19 at 20:58
  • the second route you mentioned, I have to make API request again and get the USD value only? Can't I edit the first and use the usd value only? – Zak Sep 01 '19 at 21:14
  • 1
    You need a second route because the first is sending the result view / HTML document. Use something like `app.get("/usd", ...` and `res.sendJSON(data.bitcoin.usd);`, then use `fetch('/usd').then(r => r.json()).then(usd => document.getElementById("showPrice").innerHTML = usd);` in your client side script. –  Sep 01 '19 at 21:17
  • If the API supports CORS, you can of course make the API request directly from the client side. –  Sep 01 '19 at 21:19

1 Answers1

1

Initial answer

Your setInterval works fine, it's just that inside your function the data never changes.

To fix it you have to reference a variable (of which the content changes), rather than hardcoding the value in your function.

Extra explanation

For example you are using EJS, which is a templating language. A templating language parses output based on your variables (once per page load).

Your template line

document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;

parses into

document.getElementById("showPrice").innerHTML= 9624.46;

And your interval then updates the innerHTML of #showPrice with that value, every 500 ms.

What you probably mean to do is make the request from the client (the browser), then store its response into a variable, say latestResult, and then code your function to reference that variable, like so:

document.getElementById("showPrice").innerHTML= latestResult;

Example implementation

This means that your express application (app.js) will render result without data:

app.get('/', function(req, res) {
  res.render('result');
});

And the request part will be in your template:

function updateLatestPrice() {
  fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true').then((result) => {
    const latestResult = result.bitcoin.usd

    document.getElementById("showPrice").innerHTML = latestResult || 'failed'
  })
}

setInterval(updateLatestPrice, 3000)

Note that I changed request into fetch here because I couldn't be sure whether your client code has babel, so I went with the browser's native Fetch API.

Webber
  • 4,672
  • 4
  • 29
  • 38
  • 1
    Hi @Zak, I have updated my answer to describe what is happening in more detail. – Webber Sep 01 '19 at 21:07
  • Thanks it makes sense now. But as far as I've understood, I can make the request from client side, store it in a variable and use that variable in my express app? Is that it? – Zak Sep 01 '19 at 21:12
  • 1
    If you mean whether or not you can declare this in your EJS file, then yes; the whole `request` part will be in your ejs file, so that your browser can make the request instead of the server. – Webber Sep 01 '19 at 21:16
  • I understand completely now. but I have a last question. To make a request from client side(in my case in ejs file), I have to do it by using fetch() right? – Zak Sep 01 '19 at 21:22
  • Correct, I updated my answer to be a bit more complete. – Webber Sep 01 '19 at 21:30