Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Why does the PUT method throw an error when the GET method doesn't?

I have extracted two functions from my app that are similarly constructed and the code block that calls them both. One function is a HTTP GET that returns a file 'keys.json'. The other is a HTTP PUT that sends an empty version of 'keys.json' back to the API.

The GET

const putKeysJson = async () => {
        //  this is the opening put back of an empty keys.json file
        //  putting this file back empty at the start of the run prevents
        //  double processing of JsonData files because there is an empty list of keys
        //  therefore nothing can be selected for processing
        //  @ EOJ, keys.json will be updated with any keys that may have failed to process
        const options = {
            method: 'PUT',
            uri: baseURL + keysID,
            resolveWithFullResponse: true,
            body: {
                keys: []
            },
            json: true // Automatically stringifies the body to JSON
        };
        return (await rpn(options)).body;
};

The PUT

const putKeysJson = async () => {
    //  this is the opening put back of an empty keys.json file
    //  putting this file back empty at the start of the run prevents
    //  double processing of JsonData files because there is an empty list of keys
    //  therefore nothing can be selected for processing
    //  @ EOJ, keys.json will be updated with any keys that may have failed to process
    const options = {
        method: 'PUT',
        uri: baseURL + keysID,
        resolveWithFullResponse: true,
        body: {
            keys: []
        },
        json: true // Automatically stringifies the body to JSON
    };
    return (await rpn(options)).req.Request.body;
};

The calling block:

rtProcess.get('/process', async (req, res) => {
    const jsonKeysList = await (getKeysJson());
    console.log("GET json keys", jsonKeysList);
    try {
        const keys = await (putKeysJson());
        console.log("PUT empty keys", keys);
    } catch(err) {
        console.log('Error',err.statusCode, err.name);
    }

    //
    // other code
    //
    res.render("process");
}); // end of process route

Here is my console output:

GET json keys [ '5sM5YLnnNMN_1540338527220.json',
  '5sM5YLnnNMN_1540389571029.json',
  '6tN6ZMooONO_1540389269289.json' ]
Error 404 StatusCodeError

Showing the GET to be successful and the PUT taking the catch(err) block. And, why a 404.

It appears you posted the put twice. The code under The GET is the same as under The PUT.

3 Answers

I completely revised this API. I forgot to close out this request. Sorry.

Brendan: This is an API that is my creation for my client.

Kris: You are absolutely correct. I am not only a rookie, I am not very careful either. I copied the PUT function into both examples. Here is the GET function:

const getKeysJson = async () => {
    const options = {
        method: 'GET',
        uri: baseURL + keysID,
        headers: { 'User-Agent': 'Request-Promise' },
        json: true // Automatically parses the JSON string in the response
    };
    return (await rpn(options)).keys;
};

Here is one thing I figured out (one of those AH HAH moments). The baseURL is prefixed by '/static', which as I understand it, is for serving up static files. I have been looking at this for a week thinking 'Yeah, static is what I am dealing with here.' But then, that was when I was only looking at the GET method. The minute I tried to use the PUT method, the file is no longer static, is it! That would explain why the GET worked and the PUT doesn't. A case of going brain-dead and not connecting the dots, how embarrassing.

So, I moved the quizdata folder from public up to the root (so I could maintain the integrity of the express

app.use('/static', express.static(path.join(__dirname, 'public')));

command. I changed the base url to declare the entire path as:

'http://localhost:3000/Users/doug5solas/sandbox/...myclientname.../server/quizdata/'

Instead of using the '/static' shorthand notation.

I think that should take care of the 404 error on the PUT. But now I am getting a 404 on the GET, so maybe not. However, the new error also includes and Unhandled Promise Reject Warning so I probably need to either use a try/catch or a promise resolve. But I am blathering at this point.

Brendan:

Very cogent question. I don't really see how to utilize an express PUT route in this API. There may be one but I don't know how to bind to it. Here is a link to a gist with the entire file in it. You can see the I have put a placeholder PUT route in, at the bottom of the file. But I see no way to bind to that route.

https://gist.github.com/dhawkinson/b40f6936caafe48d7be41551119f9817

I am also including links to two images that graphically show my conception of the API. One is a higher level overview and the other is more of a drill down. I am trying to do something a bit on the unusual side, I agree, but I can't believe there has never been someone that has had to tackle a similar problem with Node/Express in the past.

https://www.dropbox.com/s/ctddgv21pzai4ot/storylineDataCapture.jpg?dl=0

https://www.dropbox.com/s/t2flc7xkx320btq/storylineDataCaptureExplained.jpg?dl=0

If you have some ideas, I am all ears.

I think what you are driving at, is something more like this:

module.exports = (rtProcess) => {
    rtProcess.get('/process', async (req, res) => {

        const jsonKeysList = await (getKeysJson());
        console.log("GET keys.json", jsonKeysList);

        res.render("process");  //  this may need to be somewhere else
    }); // end of get route

    rtProcess.get('/process/failedKeys', async (req, res) => {

        //  code here

    }); // end of get failedKeys route

    rtProcess.get('/process/jsonData', async (req, res) => {

        //  code here
        //  loop on the keys (outcomeID) -- get and parse JSON, then write SQL
        jsonKeysList.forEach(outcomeID => {
            console.log(outcomeID);
            getJsonData(outcomeID);

            parseLearner();
            parseCourse();
            parseOutcome();
            parseOutcomeDetails();

            /*==================================================
                All data has been gathered -- now write it
            ==================================================*/

            log = [];
            writeLearnerSQL();
            writeCourseSQL();
            writeOutcomeSQL();
            writeLogSQL();

        });

    }); // end of jsonData route

    rtProcess.put('/process/emptyKeys', async (req, res) => {

        //  code here
        const keys = await (putKeysJson());
        console.log("PUT empty keys", keys);

    }); // end of emptyKeys route

    rtProcess.put('/process/failedKeys', async (req, res) => {

        //  this routine kicks in if any Outcome transactions fail to commit.
        //  the outcome key if written back the Keys list for reprocessing
        //  after troubleshooting the problems
        //  hopefully this never happens
        if (keysFailed) {
            putKeysFailed(keysFailed);
        }

    }); // end of put failedKeys route
};

But I'm not sure how to pull that off since the only route in this example that is tied to an <a> tag/ href is the very first one.

Brendan Whiting
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Brendan Whiting
Front End Web Development Techdegree Graduate 84,736 Points

The put request that's failing was being sent to baseURL + keysID I believe. On the express side, none of the put routes seem to handle an id paramater.