Aletho
Aletho

Reputation: 117

Express JS sends undefined JSON variables to client

I'm working on learning Express JS and practicing security. I have moved some coupon code logic to the server, to make it more secure. That means that I send a coupon code to the server, it checks whether it is a valid code, and then returns a JSON object with a message and a win indicator (1 for the win, 0 for loss).

My issue is that I cannot figure out how to get both the message and the win indicator on the client from the response. It's always undefined

This is the client:


window.addEventListener("load", () => {
    const nameInput = document.getElementById("name");
    const couponCodeInput = document.getElementById("coupon-code");
    const button = document.getElementById("button");
    const nowinElem = document.getElementById("no-win");
    const winElem = document.getElementById("you-won");

    button.addEventListener("click", async e => {
        e.preventDefault();
        winElem.style.display = "none";   //new click of button:
        nowinElem.style.display = "none"; // hide previous messages
        let resp = await fetch('winner', { 
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ name: nameInput.value, code: couponCodeInput.value }) 
        });
        
        if (resp.status !== 200) console.log('Winner report failed');        

        var res = resp.body;

        if(res.win == "1"){
            winElem.style.display = "block";
            winElem.innerText = res.msg;
        } else {
            nowinElem.style.display = "block";
            nowinElem.innerText = res.msg;
       }
            
    })
})

And this is the server:

const express = require('express')
const fs = require('fs')
const app = express()

const html = fs.readFileSync('coupon-client.html');
const js = fs.readFileSync('coupon-client.js');

const winnerCodes = ["123", "secret", "abc321"];

app.get('/', (req, res) => {
    res.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
    res.end(html);
 })
app.get('/coupon-client.js', (req, res) => {
    res.writeHead(200, {"Content-Type": "application/javascript"});
    res.end(js);
})
app.post('/winner', express.json(), (req, res) => {//use built-in JSON middle-ware
    let jsonObj = req.body //JSON already parsed: { "name": "my name" }
    if(winnerCodes.includes(jsonObj.code)){
        console.log(`Congratulations to ${jsonObj.name}!`);
        console.log("We'll send you a diploma.");
        res.json({msg: 'Congratulations - and thanks!', win: '1'});
    } else {
        console.log(`Condolences to ${jsonObj.name}!`);
        console.log("We'll send you nothing.");
        res.json({msg: 'Sorry, you did not win - but thanks for playing!', win: '0'});
    }
}) 

app.listen(8080, () => console.log('Listening...'))

As I mentioned above, I cannot get the JSON data object to give me the win and message variables. I have tried to do "JSON.parse(resp.body)" but then it gives me an error saying "unexpected character at....", which I read means that the body is already parsed, so I'm just taking the body now.

Can anyone give me a hint or help me out?

Upvotes: 1

Views: 675

Answers (1)

Emiel Zuurbier
Emiel Zuurbier

Reputation: 20944

Reading a Response object works differently from how you currently are using it.
Use the json() method on the response object. It will return a promise which parses the response from JSON into usable data.

About the Response object and its usage.
Currently you're accessing the body property on the Response object, which it inherited from the Body mixin.

The body read-only property of the Body mixin is a simple getter used to expose a ReadableStream of the body contents. MDN

So you're accessing a ReadableStream object and try to read the win property from it, which doesn't exist on the stream.

To help with this the Body mixin has methods to convert the stream into usable data. Examples are Body.json() and Body.text(). These methods read the stream and convert the body into an object, array, string or number when it has a JSON structure or into a single string, which is useful when you're sending HTML or just raw text.

Both methods return Promises in which you have to wait for the result to be ready for usage.

let resp = await fetch('winner', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: nameInput.value,
    code: couponCodeInput.value
  })
});

if (resp.status !== 200) console.log('Winner report failed');

// Decode body from JSON.
let { win, msg } = await resp.json();

if (win === "1") {
  winElem.style.display = "block";
  winElem.innerText = msg;
} else {
  nowinElem.style.display = "block";
  nowinElem.value = msg;
}

Upvotes: 2

Related Questions