serviejs / popsicle

Simple HTTP requests for node and the browser
MIT License
246 stars 19 forks source link

Popsicle crashes on text/plain response #125

Closed ejoebstl closed 5 years ago

ejoebstl commented 6 years ago

I'm currently using a REST API usually returns JSON, but returns text when anything bad happens (like 403 access denied or 500 internal server error).

However, text/plain responses lead to popsicle throwing an exception when using the json plugin.

PopsicleError {cause: undefined, code: "EPARSE", popsicle: Request, message: "Unhandled response type: text/plain", name: "PopsicleError", …}

This is inconvenient: I would rather like to check for the returned HTTP status and present an appropriate message to the user. This is currently impossible, as I cannot access the response when an exception is thrown.

Is there any way to change this behavior?

blakeembrey commented 6 years ago

It’s all just functions, so if it’s not going to be returning JSON and you know this, you could skip the parsing as JSON step.

ejoebstl commented 6 years ago

Thats the problem - I don't know if the request is going to return JSON or some text (because of an error).

blakeembrey commented 6 years ago

That’s not true, like I said - you can check before you parse as JSON. If the plugin returned text when you’re expecting a JSON response, it would be very confusing and hard to type check. That’s why it works like this. If you share the code you have right now, I can show how you can do what you’re after.

ejoebstl commented 6 years ago

The code is very basic. I constructed a minimal example below.

  private async getData(url: string) {
    const result = await popsicle.request({
      method: 'GET',
      url: api.base + url,
      })
      .use(popsicle.plugins.parse('json'));

    if (result.status !== 200) {
      alert("Oh no. Something bad happened.");
    }
    return result;        
  }

I don't know how I could use another plugin to check the content type to skip the JSON parsing.

blakeembrey commented 6 years ago

So you can always write code like:

const result = await popsicle.request({
    method: 'GET',
    url: api.base + url,
})
.use(function (req) {
    if (isTrue) {
        return popsicle.plugins.parse('json')(req);
    }

    return somethingElse;
});

If it helps, I can look at turning this into a small module you can reuse such as fn(req => isTrue, parse, somethingElse).

ejoebstl commented 6 years ago

Thank you, that example is fully sufficient. I will think a bit about solving the situation nicely and then post my solution here as well.