elbywan/wretch

Modify request and response

biesbjerg opened this issue · 4 comments

Hi!

I'm looking to create a reusable api instance that automatically:

  • changes keys in request json from camelCase to snake_case
  • changes keys in response json from snake_case to camelCase
  • revives dates (essentially detects if a something is a date and modifies it to a Date object)

I've been looking through the examples and the code and I don't think wretch provides any hooks for modifying the request and response data?

Hey @biesbjerg,

I've been looking through the examples and the code and I don't think wretch provides any hooks for modifying the request and response data?

Not directly indeed since fetch streams the body and wretch does not make any assumption regarding the format (json, blob, text…) - or the platform/polyfill (undici, node-fetch, browser, node, deno, bun…).

With that said it seems quite easy to achieve that using middlewares:

// Dummy methods for the sake of the example
const snakeize = obj => ({ ...obj, snakeized: true });
const camelize = obj => ({ ...obj, camelized: true });

const middleware = next => async (url, opts) => {
  const options = {
    ...opts,
    body: opts.body ? JSON.stringify(snakeize(JSON.parse(opts.body))) : undefined
  }
  const response = await next(url, options);
  if (response.ok) {
    const json = await response.json();
    return new Response(JSON.stringify(camelize(json)), response);
  } else {
    return response;
  }
};

// Register the middleware
const api = wretch("https://jsonplaceholder.typicode.com").middlewares([middleware]);

api.url("/todos/1").get().json(console.log);
// incoming json gets camelized
// Object { userId: 1, id: 1, title: "delectus aut autem", completed: false, camelized: true }
api.url("/todos").post({ title: "foo", body: "bar", userId: 1 }).json(console.log);
// outgoing json gets snakeized, response gets camelized
// Object { title: "foo", body: "bar", userId: 1, snakeized: true, id: 201, camelized: true }

Thanks! That would do it!

Do you have a suggestion for centrally converting date strings to Date objects? Seems I can't use this approach since middlewares return a new response with the JSON stringified?

Bonus question: I see that setTimeout (part of the AbortAddon) is used after get(), post() etc, which means I can't set a timeout for all requests — is that right?

Do you have a suggestion for centrally converting date strings to Date objects? Seems I can't use this approach since middlewares return a new response with the JSON stringified?

Bonus question: I see that setTimeout (part of the AbortAddon) is used after get(), post() etc, which means I can't set a timeout for all requests — is that right?

I think both are possible using the .resolve method, if you know in advance the chain of actions to perform after the request gets retrieved.

const snakeize = (obj) => ({ ...obj, snakeized: true });
const camelize = (obj) => ({ ...obj, camelized: true });

const api = wretch("https://jsonplaceholder.typicode.com")
  .addon(AbortAddon())
  // .defer might also be easier then writing a middleware
  .defer((w, _url, opts) => {
    return w.options({
      ...opts,
      body: opts.body
        ? JSON.stringify(snakeize(JSON.parse(opts.body)))
        : undefined,
    });
  })
  .resolve((resolver) => resolver.setTimeout(1000).json(camelize));


api
  .url("/todos/1")
  .get()
  /* the resolver inserts: .setTimeout(1000).json(camelize) here */
  .then(console.log);
api
  .url("/todos")
  .post({ title: "foo", body: "bar", userId: 1 })
  /* the resolver inserts: .setTimeout(1000).json(camelize) here */
  .then(console.log);

// And to clear the resolver, in case you need to do something else after receiving the response:
wretch()
  .url("/todos/1")
  .resolve((r) => r, true)
  .get()
  .json(console.log);

Thanks! Very helpful! 😊