Code Monkey home page Code Monkey logo

wretch's People

Contributors

am1rb avatar bb010g avatar bendingbender avatar canrau avatar dankochetov avatar dependabot[bot] avatar elbywan avatar eltociear avatar grushetsky avatar gutenye avatar holi0317 avatar izayl avatar jamiew avatar jsaraiva avatar juliuxu avatar timgates42 avatar turrea avatar unprofound avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wretch's Issues

Resolving an original request

I'd like to decorate the response object with some custom data after an original request retry resolves. Something like this:

const refreshAndRetry = async (error, originalRequest) => {
  const token = await wretch()
    .url('/auth/refresh')
    .get()
    .json();

  return originalRequest
    .resolve(resolver =>
      resolver.res(response => {
        response.token = token;
        return response;
      })
    )
    .auth(`Bearer ${token}`)
    .get()
    .unauthorized(error => {
      throw error;
    })
    .json();
};

As you can see - I'd like to have a response.token property on a request which had to refresh the token and retry. I need this to handle fresh tokens somewhere in the higher level of the application.

Unfortunately the above code throws a originalRequest.resolve(...).auth(...).get(...).unauthorized is not a function error.

Could you please help and tell me what I'm doing wrong and how can I solve this?

Abort requests

Use AbortController to add timeout and abort features.

Something like :

let w = wretch("...")
  .signal("...") // optional - ability to use a custom AbortSignal to abort multiple requests
  .get() 
     // before the fetch() call, the signal should be injected by wretch
     // if no signal was added, wretch creates an AbortController for the request
  .setTimeout(number /* aborts after N milliseconds */)
  .onAbort(AbortError => /* new catcher for aborted requests */)

let [ c, w ] = w.controller() /* ugly :( but seems like there is no better way of doing things */
 
w.res(r => /* should never be called ... */)
c.abort()

Retry mechanism

I've just started using wretch and noticed the nice feature that allows adding custom middlewares for implementing cross-cutting concerns like caching, as you've nicely illustrated in the docs. Many times, there is a need for adding support for retry mechanism. Would it be possible to illustrate in a sample how a request retry middleware would look like?

Having an issue with setting timeouts

If I do a simple POST I have no problems:

wretch(url)
  .json(jsonMessage)
  .post()
  .res()
  .catch(error => console.log(error))

The moment I try to use the timeout syntactic sugar (and setting a timeout) like this:

wretch(url)
  .json(jsonMessage)
  .post()
  .setTimeout(1000)
  .timeout(err => console.log(err.status))
  .res()
  .catch(error => console.log(error))

I get the following error and I can't figure out why:

TypeError: Cannot read property 'abort' of undefined
resolver.ts:128
    at Timeout._onTimeout (/Users/ebuchan/Source/Repos/slackapp-ops/node_modules/wretch/dist/bundle/wretch.js:1:4954)
    at ontimeout (timers.js:498:11)
    at tryOnTimeout (timers.js:323:5)
    at Timer.listOnTimeout (timers.js:290:5)

TypeScript issue when import

When I import wretch according to the test file and polyfill it as the following,

import * as formData from 'form-data';
import { URLSearchParams } from 'url';
import nodeFetch from 'node-fetch';
import wretch from 'wretch';

wretch().polyfills({
  fetch: nodeFetch,
  FormData: formData,
  URLSearchParams,
});

it complain that wretch.default is not a function when build with tsc.

wretch.default().polyfills({
TypeError: wretch.default is not a function

And when i change it to the syntax below,

import * as wretch from 'wretch';

it works, but the linter is complaining...

[ts] Cannot invoke an expression whose type lacks a call signature.

So what I do now is

import * as wretch from 'wretch';
import { Wretcher } from 'wretch/dist/wretcher';

((wretch as any)() as Wretcher).polyfills({
  fetch: nodeFetch,
  FormData: formData,
  URLSearchParams,
});

so that the type definition and tsc build keep working again.

Is there anything wrong with my syntax? Or is there any compiler option that is causing this?

How to catch a network error with cors (i.e: net::ERR_NAME_NOT_RESOLVED)

Hi @elbywan, first of all thank for your nice work.

I could not find a way to catch network errors (net::ERR_NAME_NOT_RESOLVED -> failed to fetch) in cors mode. Using a middleware I can log them. Imagine the exampe below:

const logMiddleware = () => next => (url, opts) => {
    console.log(opts.method + "@" + url);
    return next(url, opts);
}

const wretchRequest = wretch()
    .options({ credentials: 'include', mode: 'cors' })
    .options({ headers: { Accept: 'application/json' } })
    .middlewares([
        logMiddleware()
    ]);

wretchRequest
           // AN INVALID URL (to provoke network failure)
            .url(`http://qsdkjlkjqslkdjlkqjsldkjqs.com/auth/token`)
            .options({ mode: 'cors' })
            .post({
                email: email,
                password: password,
            })
            .json(response => {
               // Here some code. 
            })
            .catch(error => {
               alert('THE NETWORK ERROR SHOULD BE REPORTED HERE ?');
               alert('UNFORTUNATELY, IT DOES NOT');
            });

The logMiddleware correctly shows the failure during the 'pre-flight/option request',

> OPTIONS http://qsdkjlkjqslkdjlkqjsldkjqs.com/auth/token net::ERR_NAME_NOT_RESOLVED
> Login error TypeError: Failed to fetch

But it's not reaching the catch block. Any Idea how to handle this in the catch ?

Clarification re: try/catch, and .catch method

I will preface by saying: @elbywan thanks your work on this project. It has made working with fetch so much more palatable, and the usability of this abstraction is wonderful.

I have some code that looks like this:

async function refresh() {
  const accessToken = getAccessToken();

  try {
    await wretch(`path/to/oauth/refresh`)
      .options({
        credentials: 'include',
      })
      .auth(`Bearer ${accessToken}`)
      .get()
      .catch((e) => {
        // this is never called
        debugger;
      });
  } catch (e) {
    // this is also never called
    debugger;
  }

  debugger;
}

I'm testing a flow where a user's refresh token is absent or invalid, and so I expect this refresh endpoint to fail with a 400. When I call refresh(), it seems that the third debugger statement is encountered first, and what is more, the Promise returned by Wretch hasn't even returned yet. In fact, the two other debugger statements are never even encountered, even when the request returns as a 400.

I must be missing something about the control flow of Wretch. Does anything stick out?

Polling requests (custom middleware)

Hi!

Firstly, thanks for a great library - really useful!

I would like to create a custom middleware, which would call the same request again at a later time (as interval - for polling). For example: in options() I would specify needed behavior:

return .api
   .url(urlPath)
   .options({
      refetchDelay: 2000,
      refetchUntil: () => this.refetchEnabled
   })
   .get()
   .json(({ data }) => {
      this.applications.set(data);
   });

The problem is that a single request should be able to produce multiple requests. Having something like this works that it refetches the request, but the callback (json()) is not being executed (not being resolved):

export const refetch: RefetchMiddleware = () => {
    return next => (url, opts) => {
        const refetchRequest = (response: any): any => {
            const refetchDelay = opts.refetchDelay || defaultDelay;
            const refetchUntil = opts.refetchUntil || defaultUntil;

            if (refetchUntil()) {
                return new Promise(resolve => {
                    setTimeout(() => {
                        resolve(next(url, opts).then(refetchRequest));
                    }, refetchDelay);
                }).then(refetchRequest);
            }

            return response;
        };

        return next(url, opts).then(refetchRequest);
    };
};

Thoughts?

Thanks,
Mitja

Timeout timers should be cleared when requests are finished

Hi, thanks for this great library.

I don't know whether this should be considered as a bug, but I see that timeout timers are not cleared even if the request promise has been resolved or rejected. Here is the relevant code line.

If you need an idea of how this can be fixed look at the following axios code snippet.

I think it's important to clear timers so that resources are not wasted without any purpose. As far as I can see, to implement this feature we need to call clearTimeout in the following cases:

  • Promise resolved
  • Promise rejected
  • AbortController called abort

Difference to other frameworks?

I just came across wretch. And from the description, it sounded a bit like r2 for example.
When it comes to xmlhttprequest/fetch wrappers I'm always asking myself, how is this framework different from the others? I could still use axiom for example. Sure it still uses the oldie but goldie xmlhttprequest instead of fetch. But it also handles the 404 stuff and json repsonses and so on.

`.query()` should accept a string

Some web applications use non-standard query strings that aren't necessarily an ampersand-separated list of keys and urlencoded values.

wretch should allow this case by accepting a string value in .query() and appending that to the URL after the ? verbatim.

Wretch does not throw on network errors

I'm working on a mobile hybrid app. When the user is offline and the request fails fetch does not throw an error but neither does wretch. What happens is that the response is considered ok and it obviously can't be json parsed.

When using fetch it's common to use the response.ok flag to prevent this but from what I've seen this is not available in wretch.

What is the appropriate solution to this with wretch?

Should wretch throw an error when the ok flag is false?

Is there a way I can access the native fetch response?

Get response headers

Hi and thanks for the awesome lib!

Is there a way to get response headers when using wretch? I wasn't able to find anything in the docs.

Can't return falsy value from response handler callback

Hey,

From the documentation:

wretch("...").get().json(() => "Hello world!").then(console.log) // Hello world!

This does not work if the returned value is false or null (probably any falsy value), the original request is passed on to the next promise instead.

I took a quick look at the source and I believe it's caused by how the callback is run here

https://github.com/elbywan/wretch/blob/master/src/resolver.ts#L89-L92

I'm not sure if it's an intentional choice, so when the callback does not return you won't lose the original request, or a bug. Even if it's intentional it might be better to explicitly check if the return value is undefined as returning some kind of "success" boolean might be a common use case.

Fortunately the workaround is pretty straight-forward:

wretch("...").get().json().then(() => false).then(console.log)

Thanks for this very nice library btw!

res() value after error

Hi

Is there a way to enable an error to be passed to a response handler? for example have the res() object status equal to the error code?

In any case, thanks for this amazing library!

Hitting timeout that is set - but syntactic sugar not catching it

Hey there. Loving usage of wretch. So simple to use.

I have a call to an endpoint where it was hitting the timeout, but it was not using the timeout syntactic sugar, but instead being caught by the catch().

wretch
  .url(endpoint)
  .formUrl(form)
  .post()
  .setTimeout(8000)
  .timeout(error => console.log('Timeout for ' + endpoint + ' ' + error))
  .json(response => console.log(response)
  .catch(error => console.log('Error: ' + error))

The endpoint had started failing as it was being slow. In this case I was getting as below indicating the .timeout isn't catching the abort from the 8000ms.

Error: AbortError: Aborted for /sdpapi/request?format=json

When I bumped the timeout to 20000ms It logged the response. Stupid software upgrade on the target system made it much slower :(

Shouldn't .timeout() catch the abort?

Why do we have to pass AbortController instead of AbortSignal?

Hi!
I very like this library, I was missing such a wrapper over fetch 👍
The problem that I have is that currently using fetch, I developed my services to optionally accept AbortSignal as a parameter to control cancellation from the outside. This library doesn't accept AbortSignal but AbortController - by doing this is not compatible with fetch implementation.

Is there a reason why you implemented it that way?

The fragment that is responsible for that is:

    /**
     * Associates a custom signal with the request.
     * @param controller : An AbortController
     */
    signal(controller: AbortController) {
        return this.selfFactory({ options: { ...this._options, signal: controller.signal }})
    }

My suggestion is to change it to:

    /**
     * Associates a custom signal with the request.
     * @param signal : An AbortSignal
     */
    signal(signal: AbortSignal) {
        return this.selfFactory({ options: { ...this._options, signal }})
    }

Unfortunately it would introduce a breaking change. What do you think about it? 😃

res() usage question

I've noticed the readme uses res() in a few places, without using the response (I mean .res(response => do something here with response)). Here's one example:

wretch().url("...").get().json(/* ... */)

// Can be used to set a base url

// Subsequent requests made using the 'blogs' object will be prefixed with "http://mywebsite.org/api/blogs"
const blogs = wretch("http://mywebsite.org/api/blogs")

// Perfect for CRUD apis
const id = await blogs.post({ name: "my blog" }).json(_ => _.id)
const blog = await blogs.url(`/${id}`).get().json()
console.log(blog.name)

await blogs.url(`/${id}`).delete().res()

// And to replace the base url if needed :
const noMoreBlogs = blogs.url("http://mywebsite.org/", true)

I'd like to understand what res() does at the end of await blogs.url(`/${id}`).delete().res().

Retrying request, but wait until the user click on a button

Hi @elbywan, I have a similar use case like #16 but instead of retrying automatically I want to wait for the user to press a button before running the next request.
I try to use the replay() method as in the docs originalRequest.replay() but it does not have the same resolver as the original one;
Can you show me how to achieve this?

handling error before .[response type]()

First of all, awesome API!

this works wretch().get().error() but not this wretch().error().get() gives .error is not a function.

How can I setup a default error handler, before calling get/post/etc?

Issue with Webpack

Hi,

I have an issue when using your library on a typescript project using webpack.

When I call Post, I have this error in Chrome :

TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation

After looking for the error on google, I came across this issue which I think is similar.

Weird NetworkError with Firefox

Hi @elbywan, thanks a lot for your work !

I face a weird error with Firefox (no errors happen in Chrome and Safari), which cause the browser to refresh.
Do you know the origin of the problem ? Format of body ?

Thanks a lot for your help !

const addAuthTokenMiddleware = next => (url, opts) => next(url, {
    ...opts,
    headers: {
        ...opts.headers,
        Authorization: `Bearer ${getToken()}`,
    },
});

const defaultWretch = wretch()
    .url('/api')
    .options({ credentials: 'include' })
    .middlewares([addAuthTokenMiddleware]);


const response = defaultWretch
    .url(url)
    .post(body)
    .json()
    .catch(error => {
        console.error('Clovis ServerApiJSON post error :', {
            error: error,
            url: url,
            body: body,
        });
    });

image

convertFormUrl behaves unexpectedly with arrays

https://runkit.com/embed/gh39bs0gaifx:

var wretch = require("wretch");
var qs = require("qs");

const convertFormUrl = (formObject) => {
    return Object.keys(formObject)
        .map(key =>
            encodeURIComponent(key) + "=" +
            `${ encodeURIComponent(typeof formObject[key] === "object" ? JSON.stringify(formObject[key]) : formObject[key]) }`)
        .join("&")
};

const obj = {'a': [1, 2]};
console.log(convertFormUrl(obj), unescape(convertFormUrl(obj)));
console.log(qs.stringify(obj), unescape(qs.stringify(obj)));

==>

"a=%5B1%2C2%5D"
"a=[1,2]" // wretch
"a%5B0%5D=1&a%5B1%5D=2"
"a[0]=1&a[1]=2" // qs

I expect something like the second.

Don't send referrer header

Hey, I really the lib but I think I found a strange behavior.
I would like to avoid sending the Referrer header but it doesn't seem to work as expected.
My instance is like this:

var apiRoot = wretch(root)
  .options({credentials: "include", mode: "cors", referrer: 'no-referrer'})
  .content("application/json")
  .accept("application/json")

And when I do apiRoot.get().json(), the Referrer header is set to Referer: http://localhost:8080/apps/no-referrer
I also tried with false and null, but the string version is added to the root url.
Am I doing something wrong? I know fetch accepts that option.

Thanks

Improve `.query()` function to handle edge cases

Intro

On my actual project, I use wretch.

Don't get me wrong, wretch is awesome: it is one of the best designed fetch wrapper I have seen so far and I have been using so far and I've been using a few (XMLHTTPRequest direct, axios, request, superagent, fetch directly, frisbee and now wretch).

Before everything, thank you. 😍

Problem I try to solve

.query() is misleading. It try to handle stringification / url encoding of javascript object and manage to do it well for 80% of the cases (common one) but fails the complex one.

On my project for instance we have done:

wretch("https://example.com/test").query({ pagination: { pageSize: 25, page: 1 }}).get()

which outputed : https://example.com/test?pagination[object Object] 😱

That cause us a bug and took a while to debug (2h or so).

I plan to improve wretch so users does not have to be wretch finding bugs.

Reference implementation

For me the reference implementation is https://github.com/ljharb/qs and looking at https://github.com/ljharb/qs/blob/master/test/stringify.js it supports the following features (checkbox checked if wretch support it right now)

  • stringify a string is indempotent a=b-> a=b
  • stringify not html feature like { a: '€' } -> 'a=%E2%82%AC'
  • stringify object with space in key or value { 'a a': 'b 'b'} -> 'a+a=b+b'
  • stringify nested object like { a: { b: 'c' } } -> 'a%5Bb%5D=c'
  • stringify nested object with dot notation { a: { b: 'c' } -> 'a.b=c'
  • stringify array value with indice notation { a: ['b', 'c']} -> 'a[0]=b&a[1]=c'
  • stringify array value with repeat notation { a: ['b', 'c']} -> 'a=b&a=c'
  • stringify array value with bracket notation { a: ['b', 'c']} -> 'a[]=b&a[]=c'
  • stringify object skipping null/undefined value { a: 'b', c: null} -> 'a=b
  • stringify date using toIsoDate
  • ...

Next steps

If it is ok for you:

  • I will create a PR that will add failing tests #34
  • you will determine what case you want to support in wretch (do we intend 100% compatibility with internet standards, if not how do we plan to let the user "hook" encoders to support proper encoding)
  • depending on that decision, we decide to code the missing part or rely on https://github.com/ljharb/qs
  • I code the missing part, with your help and review

Note

I'm support use to contributing to repo with prettier activated. Here I needed to deactivate it then do the formatting by hand which is painful now that I get use to format.

Would you mind an other PR to include prettier and configure it to be the less different to actual source code ?

Broken example in docs

The reAuthOn401 example in the readme is just what I was looking for, but I had to adjust it a little to work correctly. The original:

const reAuthOn401 = wretch()
  .catcher(401, async (error, request) => {
    const token = await wretch("/renewtoken").get().text()
    storeToken(token)
    // The return of `.replay()` is just a vanilla Promise, which has no `.unauthorized()` method
    return request.auth(token).replay().unauthorized(err => { throw err }).json()
  })

Updated:

const reAuthOn401 = wretch()
  .catcher(401, async (error, request) => {
    const token = await wretch("/renewtoken").get().text()
    storeToken(token)
    return request
      .auth(token)
      .catcher(401, (errInner, reqInner) => {
        throw errInner;
      })
      .replay()
      .then(resp => resp.json());
  })

Unless I'm missing something?

Also, thanks for making the fetch library I always wanted!

Catchers not found on reusable wretch instance

Hey, great library!

Question: I've set up a reusable wretch instance that looks somewhat like the following. Also, I'm having a weird time using wretch without having to use default, see below:

const wretch = require("wretch").default;

const csrfMiddleware = next => (url, opts) => {
  const csrfToken = window.localStorage.getItem("csrfToken");
  opts.headers = { ...opts.headers, "x-csrf-token": csrfToken };
  return next(url, opts);
};

export const apiFetch = wretch()
  .url(`/v1`)
  .accept("application/json")
  .content("application/json")
  .options({
    credentials: "same-origin"
  })
  .middlewares([csrfMiddleware])
  .resolve(resolver => resolver
    .res(response => {
      window.localStorage.setItem("csrfToken", response.headers.get("x-csrf-token"));
      return response.json();
    })
    .catch(error => {
      window.localStorage.setItem("csrfToken", error.response.headers.get("x-csrf-token"));
      throw error;
    })
  );

Then, when I try to use the notFound catcher in a subsequent call, it errors, saying that the function chain with notFound in it is not a function:

return apiFetch
  .url(`/foo`)
  .query({
    foo: 'bar'
  })
  .get()
  .notFound(() => {
    return {
      foo: `quux`
    };
  }) // this results in Uncaught TypeError: apiFetch.url(...).query(...).get(...).notFound is not a function
  .then(json => json)
  .catch(error => {
    console.error(error);
  });

Any suggestions on the right way to accomplish what I'm doing here?

Remove .baseUrl and add a chainable way to append an url

Example :

// Old way : with baseUrl
const w = wretch().baseUrl("http://prefix.com/data")
w("/1").get()
w("/2").get()
// Oops ...
w.baseUrl("http://prefix.com/data/subdata")("/1").get()

// New way : With a flag ?
const w = wretch("http://prefix.com/data")
w.url("/1", true).get()
w.url("/2", true).get()
w.url("/subdata", true).url("/1", true).get()

// New way : Or with a new method ?
const w = wretch("http://prefix.com/data")
w.addUrl("/1").get()
w.addUrl("/1").get()
w.addUrl("/subdata").addUrl("/1").get()

// Or both ?

// Or simply make .url append instead of replacing urls ?
// My favourite, even if it's a breaking change I feel like it's more intuitive this way

const w = wretch("http://prefix.com/data")
w.url("/1").get()
w.url("/2").get()
w.url("/subdata").url("/1").get()

// With a 'replace' flag
w.url("http://test.com/", true)

Async defer

Hi! I have read the docs and peeked at the source and I have a few questions.

  1. Is defer in principle a "managed" (wretcher instead of fetchlike) version of middlewares? It feels much easier to configure the wretcher instead of dotting into the options of middlewares.
  2. Can defer be async? I need to attach a token to the request but that token is not available sync (it's lazily fetched the first time a request is made).

I need something like this:

wretch()
  .defer(async w => {
    const token = await this.getAccessToken() // <--- lazy retrieval
    return w.auth('Bearer ' + token)
  })

With middleware I have this code (based on your sample code from other issues):

wretch()
  .middlewares([next => async (url, opts) => {
    const token = await this.getAccessToken()
    if (token) {
      return next(url, {
        ...opts,
        headers: {
          ...opts.headers || {},
          Authorization: 'Bearer ' + token
        }
      })
    }

    return next(url, opts)
  }]

getAccessToken ensures deduping, caching, refreshing token, etc. So I only need to call this in an async context. With an async defer it would be so simple to write. How feasible is this?

Thanks!

Pass complete request to catchers, for easier request replay

Just started using wretch and I really like the API. Currently building a large app, with a micro service architecture, and really appreciate how you can reuse a wretch object for each micro service component, with very small variations for each endpoint.

One thing that I'm missing, that would be convenient, is to have the complete wretch object for a given request, passed as an argument to the callback of any catcher.

The scenario I'm trying to address is primarily HTTP 401 responses, where my access token have expired. In that case, I would like to have a callback that can refresh the token, and then take the failed request and replay it (but with a new access token).

Pseudo-code example:

wretch("...")
  .get()
  .unauthorized((err, req) => {
    const token = getNewToken();
    // Replay the failed request, with the new token
    const response = req.auth(`Bearer ${token}`).json()
  })

Not the most useful example, but I believe it shows what I'm after.

What do you think about that?

.polyfill is global

From the documentation:

// Or the non-global way :

wretch().polyfills({
    fetch: require("node-fetch"),
    FormData: require("form-data"),
    URLSearchParams: require("url").URLSearchParams
})

There is a single config object shared between the wretch() instances, which means that it is currently impossible to use different fetch polyfills for each instance.
The use case would be to handle cookie headers for different APIs in Node.js.

eg.

class API {
  constructor (url) {
      this.api = wretch()
        .url(url)
        .options({ credentials: 'include', redirect: 'follow', cache: 'no-store' })

    // Node.js environment
    if (typeof window === 'undefined') {
      const { CookieJar } = eval('require')('tough-cookie')
      const jar = new CookieJar()
      const fetch = eval('require')('./node/fetch')({ jar })
      const { Headers, Request } = fetch
      this.api.polyfills({
        fetch,
        FormData: eval('require')('form-data'),
        URLSearchParams: eval('require')('url').URLSearchParams,
      })
  }
}

handling other errors

Hello,
Can wretch handle all other errors beside the one that I specify?

e.g.

wretch().url(...).post(....)
.unauthorized(_=>{...})
.internalError(_=>{...})
.allOtherErrors(_=>{...})

When headers is null, auth header is removed

I set the auth header like this which works fine:

api.auth(`Bearer ${ token }`);

On some requests I need to pass headers, but not others. I have a generic method for doing POST requests so sometimes headers is undefined and it seems the Authorization header is removed:

api.headers(headers)

I've resorted to doing this:

api.headers(headers || {})

It works, but it doesn't seem like .headers() should be messing with the auth header set with .auth(), no?

Weird sefFactory defaults behavior

This part of code

private selfFactory({ url = this._url, options = this._options, catchers = this._catchers,
            resolvers = this._resolvers, middlewares = this._middlewares, deferredChain = this._deferredChain } = {}) {
    return new Wretcher(url, options, catchers, resolvers, middlewares, deferredChain)
}

cause new wretches instance use its parent middlewares, resolvers ... this optimization cause weird issue with following part

    /**
     * Catches an http response with a specific error code or name and performs a callback.
     */
    error(errorId, cb) {
        catchers.set(errorId, cb)
        return responseChain
    },

which cause parent object to use error handlers from new wretches instances

for example

/**

  • Change 404 error handler
    */
    handleNotFound(): Api<"404-undefined"> {
    return this.merge({
    api1Endpoint: this.ap1Endpoint.resolve((request) => request.notFound(() => undefined)),
    api2Endpoint: this.ap2Endpoint.resolve((request) => request.notFound(() => undefined)),
    }) as Api<"404-undefined">;
    }

Mutate original this.ap1Endpoint and it start use error handler from new instance

This code fix problem

this.ap1Endpoint.catcher(404, (() => undefined)),

but you can get rid of all such problems if selfFactory won't use objects from parent, instead it could do a shallow copy of all required properties

POST submits as OPTIONS

This is my code:

        wretch(`${ENDPOINT}/people`)
          .post({
            person: {
              family_name: this.formData.lastName,
              given_name: this.formData.firstName,
              email_addresses: [
                {
                  address: this.formData.email
                }
              ]
            }
          })
          .res(res => {
              this.loading = false
              this.sucess = true
          })
          .catch(err => {
            this.loading = false
            this.error = err
          })

This is the network request that my browser submits:

Request URL:https://ENDPOINT/people
Request method:OPTIONS
Remote address:etc.:443
Status code: 200
Version:HTTP/2.0
Referrer Policy:no-referrer-when-downgrade

It also results in a CORS error, despite the server having its origin policy set to *:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://ENDPOINT/people. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

This request functions just fine in my REST client.

Async await friendly? :)

Hey there! This looks like a nice lib. Good work so far!

I wanted to know, if wretch is good with async/await. Since res is a callback and not a promise. Does wretch return a promise?

Add formUrl body type

Sets the content encoding header to "application/x-www-form-urlencoded" and sets the body.

// With a string
wretch("myurl").formUrl("say=Hi&to=Mom").post()

// Provide a simple way to serialize objects (no funky stuff)
wretch("myurl").formUrl({ say: "Hi", to: "Mom"}).post()

Roadmap to 1.0

This is my new favorite HTTP client.

Wondering if there's any Roadmap of features to implement before wretch reaches 1.0

Ability to set baseUrl

It would be nice to be able to set a baseUrl in the defaults, similar to how you can in axios:

var instance = axios.create({
  baseURL: 'https://api.example.com'
});

Awesome work, this library is just what I have been looking for!

Dist contains `const`

Hi,

When building our source with gulp and we use 'gulp-uglify' the following error occurs:

GulpUglifyError: unable to minify JavaScript
Caused by: SyntaxError: Unexpected token: keyword (const)
File: C:\dev\static\build\js\app.min.js
Line: 26603

line 26603: !function(t,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):t.wretch=r()}(this,function(){"use strict";const t=Objec ....

Thank you!

Jan

Support for proxy.

Is proxy (http[s]_proxy) supported ? Since many of us use this behind corporate firewall, it would be great if that can be done.

Consider respecting semantic versioning

On the "New Release" page, GitHub suggesting reading about semantic versioning

image

https://semver.org/

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards-compatible manner, and
  • PATCH version when you make backwards-compatible bug fixes.

DO NOT SHIP BREAKING CHANGES IN MINOR RELEASES.

Simple as that.

Don't be afraid of numbers, they are here to help us. Use numbers properly and have a nice day.

proxy support

Is there a way to use a proxy?

Normally, I would setup a HttpsProxyAgent then pass that as agent to https.get()

Set .auth() with a function on each requests

Hello!

Great work for this little package. I like the way it normalizes behaviors.

I'm currently having a problem with setting the wretch().auth() dynamically. Before I'm connecting, I don't have things to put into wretch().auth() but when I'm connected, I have a token that I could not bind to wretch().auth() because it doesn't run on every requests.

Any idea?

How to handle 204 responses ?

I've got this error with wretch and 204 response with no json in the response. How could i fix it ? Thanks a lot !

SyntaxError: Unexpected end of JSON input at eval

image

My code :

wretch()
	.url('/toto/65465874')
	.options({ credentials: 'include' })
	.delete()
	.json();

Original request method

So I'd like to create an app-wide wretch instance which will retry requests on unauthorized statuses. It would be pretty easy with wretch, but unfortunately, I don't really know how do I replay the same specific request.

Because actually sending the request requires a call to .get(), .post() or whatever method it was, I can't replay the same request having a global instance.

Am I missing something? Is there a way to send the request without calling the .[httpMethod]() function?

DELETE method with body

Hi @elbywan, wretch simplifies my code dramatically. thanks a lot.

btw, in some cases, I have to use DELETE method with the body.
For now it seems that sending a body using the DELETE method
on wretch is not allowed.

I guess DELETE with body is not prohivited by the regulation.

so if I write some PRs, could you accept it? does this make sense?

thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.