1

I'm trying to make this brief. Hopefully, it isn't so brief it makes no sense. I need to read a list of identifiers from an API that will be used to subsequently 'GET' the JSON files associated with the keys in the list. The list array is stored in another JSON file at the same endpoint with an identifier of 'keys.json' In order to prevent the list from being processed twice, I want to immediately write an empty array back to 'keys.json' upon retrieving it.

Here is the successful function that 'GETS' the list.

const getJsonKeys = 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 the unsuccessful 'POST' that I try to write with:

const postEmptyJsonKeys = async () => {

    const options = {
        method: 'POST',
        uri: baseURL + keysID,
        body: {
            keys: []
        },
        json: true // Automatically stringifies the body to JSON
    };
    return (await rpn(options)).req.Request.body;
};

Here is the block that calls them both:

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

        const jsonKeysList = await (getJsonKeys());
        console.log("Retrieved Keys", jsonKeysList);
        try {
            const req = await (postEmptyJsonKeys());
            console.log("Wrote", req.Request.body);
        } catch(err) {
            console.log(err.statusCode, err.error);
            console.log(err);
        }

        //
        //  more code here
        //
        jsonKeysList.forEach(courseID => {
            //
            // more code here
            //
        });

        res.render("process");
    }); // end of process route
};  //  end of module exports

I have tried everything I know to do to ferret out the answer in the various docs around but I can find nothing that tells me why the catch block is taken, rather than getting a successful try.

BTW the error.status code is a 404.

The error is a string that looks like HTML, which is also a mystery to me since I am trying to POST a simple:

{
    keys: []
}
doug5solas
  • 77
  • 10
  • Can you please post the error message you get? Maybe the `POST` endpoint is not properly defined in the other API you are calling and that's why you get a `404`. Have you tried sending the `POST` request with Postman to verify it's working properly? Also, if you only want to be able to `GET` that list once why don't you use `DELETE` instead of `POST` to completely remove it? – Danziger Nov 06 '18 at 03:19
  • Very good questions. The error, ' \n\n\n\nError\n\n\n
    Cannot POST /static/quizdata/keys.json
    \n\n\n'. I haven't tried Postman, but that is a good Idea. The reason I don't DELETE (though I may think about that) is because keys.json is persistent, as new testing outcomes happen, new JSON files are written to the API and so is an updated version of keys.json, utilizing a push to update the list. I might not lose anything with a DELETE. I'll think about that one.
    – doug5solas Nov 06 '18 at 16:29
  • `/static/quizdata/keys.json`... That `static` part makes me think you are serving this with `express.static` and if that's the case, then that's why you can't `POST` anything there, that middleware is not meant for that. You need to implement your own [`POST` route](https://expressjs.com/en/guide/routing.html). – Danziger Nov 06 '18 at 16:38
  • Take a look at this other question which is someone similar to yours, that might give you some clues of what you need to implement to make this work: https://stackoverflow.com/questions/53165555/appending-a-json-file-from-js-file-in-express-application/53165763#53165763 – Danziger Nov 06 '18 at 16:39
  • 1
    I appreciate the help. I don't know if I ever would have come to implementing the POST route without your input. I'm going to have to figure it out, but I think it makes sense. Is there any source I can go to to learn this stuff? – doug5solas Nov 07 '18 at 00:26
  • My answer in that other question should have most of the code you need for that. Otherwise, you can check the [official documentation](https://expressjs.com/en/guide/routing.html) or just google "nodejs handle post" or "express handle post", depending on whether you are using Express or not, and you will find plenty of tutorials and posts about that. – Danziger Nov 07 '18 at 00:43
  • 1
    @Danziger I think I just "got" what you are telling me. By using '/static' I am de facto stating I am serving a static file. I can't PUT to a static file because then it isn't static. Am I right? – doug5solas Nov 08 '18 at 00:45
  • Kind of. It's not because the route has `static` in it exactly, but you got the main idea. Please, check the answer I have posted for a more detailed answer. – Danziger Nov 08 '18 at 12:28

1 Answers1

0

So this error:

Cannot POST /static/quizdata/keys.json

Makes me think the API endpoint you are POSTing to is not properly defined, and that's why you get a 404 It's telling you there is no POST handler that matches that request.

As the question has the node.js tag and I can see the static part in the URL, that makes me think you might be serving that static content with express.static built-in middleware, and if that's the case, then that's why you can't POST anything there, as that middleware is not meant for that and will only take care of GET requests.

Regarding your comment, it's not static content because the route has static in it or because the content it's in a directory called static (if that's the case).

Take a look at this examples:

  • This will handle GET requests like http.../static/path/inside/public/dir.png:

    app.use('/static', express.static('public'));
    
  • This will handle GET requests like http.../assets/path/inside/public/dir.png:

    app.use('/assets', express.static('public'));
    
  • This will handle GET requests like http.../whatever/something/inside/public/dir.png:

    app.use('/whatever', express.static('public'));
    
  • This will handle GET requests like http.../something/inside/public/dir.png:

    app.use(express.static('public'));
    
  • This will handle GET requests like http.../something/inside/my-static-files/dir.png:

    app.use(express.static('my-static-files'));
    

You can find more examples in the official docs.

In any case, let's assume you are serving static content using this option:

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

You can still add another middleware to handle POST requests. Something like this:

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

const app = express();
const port = 3000;

...

app.post('/static', (req, res, next) => {
    // Just an example, you can replace the path and the []
    // with anything you want:
    fs.writeFileSync('path/to/file.json', JSON.stringify([]));
});

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

app.listen(port, () => console.log(`Listening on port ${port}!`));

You might also find this other question useful: Appending a JSON file from JS file in Express Application

Danziger
  • 19,628
  • 4
  • 53
  • 83