Important : Wretch is in active development ! Please check out the changelog after each update for new features and breaking changes. If you want to try out the hot stuff, look at the dev branch.
// Fetch needs a second callback to process the response body
fetch("examples/example.json")
.then(response => response.json())
.then(json => {
//Do stuff with the parsed json
})
// Wretch does it for you
// Use .res for the raw response, .text for raw text, .json for json, .blob for a blob ...
wretch("examples/example.json").get().json(json => {
// Do stuff with the parsed json
})
// Fetch won’t reject on HTTP error status
fetch("anything")
.then(response => {
if(!response.ok) {
if(response.status === 404) throw new Error("Not found")
else if(response.status === 401) throw new Error("Unauthorized")
else if(response.status === 418) throw new Error("I'm a teapot !")
else throw new Error("Other error")
}
else // ...
})
.then(data => /* ... */)
.catch(error => { /* ... */ })
// Wretch throws when the response is not successful and contains helper methods to handle common codes
wretch("anything")
.get()
.notFound(error => { /* ... */ })
.unauthorized(error => { /* ... */ })
.error(418, error => { /* ... */ })
.res(response => /* ... */)
.catch(error => { /* uncaught errors */ })
// With fetch you have to set the header, the method and the body manually
fetch("endpoint", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ "hello": "world" })
}).then(response => /* ... */)
// Omitting the data retrieval and error management parts
// With wretch, you have shorthands at your disposal
wretch("endpoint")
.json({ "hello": "world" })
.post()
.res(response => /* ... */)
// Wretch is immutable which means that you can configure, store and reuse objects
const corsWretch = wretch().options({ credentials: "include", mode: "cors" })
// Sends a cors request to http://mycrossdomainapi.com
corsWretch.url("http://mycrossdomainapi.com").get().res(response => /* ... */)
// Adding a specific header and a base url without modifying the original object
const corsWretch2 = corsWretch.url("http://myendpoint.com").headers({ "X-HEADER": "VALUE" })
// Post json to http://myendpoint.com/json/postdata
corsWretch2.url("/json/postdata").json({ a: 1 }).post()
// Reuse the original cors wretch object
const corsWretch3 = corsWretch.url("http://myotherendpoint.com").accept("application/json")
// Get json from http://myotherendpoint.com/data/1
corsWretch3.url("/data/1").get().json(myjson => /* ... */)
// Get json from http://myotherendpoint.com/data/2
corsWretch3.url("/data/2").get().json(myjson => /* ... */)
npm i wretch
git clone https://github.com/elbywan/wretch
cd wretch
npm install
npm start
Wretch is compatible with modern browsers out of the box.
For older environment without fetch support, you should get a polyfill.
Works with any FormData or fetch polyfills.
// The global way :
global.fetch = require("node-fetch")
global.FormData = require("form-data")
global.URLSearchParams = require("url").URLSearchParams
// Or the non-global way :
const fetch = require("node-fetch")
const FormData = require("form-data")
wretch().polyfills({
fetch: fetch,
FormData: FormData,
URLSearchParams: require("url").URLSearchParams
})
Wretch is bundled using the UMD format (@dist/bundle/wretch.js
) alongside es2015 modules (@dist/index.js
) and typescript definitions.
<!--- "wretch" will be attached to the global window object. -->
<script src="https://unpkg.com/wretch"></script>
// es2015 modules
import wretch from "wretch"
// commonjs
var wretch = require("wretch")
Wretcher objects are immutable.
wretch(url, options)
// A fresh Wretcher object
.[helper method(s)]()
// Optional
// A set of helper methods to set the default options, set accept header, change the current url ...
.[body type]()
// Optional
// Serialize an object to json or FormData formats and sets the body & header field if needed
.[http method]()
// Required
// Performs the get/put/post/delete/patch request
/* Fetch is called at this time */
.[catcher(s)]()
// Optional
// You can chain error handlers here
.[response type]()
// Required
// Specify the data type you need, which will be parsed and handed to you
/* Wretch returns a Promise, so you can continue chaining actions afterwards. */
.then(/* ... */)
.catch(/* ... */)
Create a new Wretcher object with an url and vanilla fetch options.
Helper methods are optional and can be chained.
Set default fetch options which will be used for every subsequent requests.
// Interestingly enough, default options are mixed in :
wretch().defaults({ headers: { "Accept": "application/json" }})
// The fetch request is sent with both headers.
wretch("...", { headers: { "X-Custom": "Header" }}).get()
// You can mix in with the existing options instead of overriding them by passing a boolean flag :
wretch().defaults({ headers: { "Accept": "application/json" }})
wretch().defaults({ encoding: "same-origin", headers: { "X-Custom": "Header" }}, true)
/* The new options are :
{
headers: { "Accept": "application/json", "X-Custom": "Header" },
encoding: "same-origin"
}
*/
Sets the method (text, json ...) used to parse the data contained in the response body in case of an HTTP error.
Persists for every subsequent requests.
wretch().errorType("json")
wretch("http://server/which/returns/an/error/with/a/json/body")
.get()
.res()
.catch(error => {
// error[errorType] (here, json) contains the parsed body
console.log(error.json))
}
Sets the non-global polyfills which will be used for every subsequent calls.
const fetch = require("node-fetch")
const FormData = require("form-data")
wretch().polyfills({
fetch: fetch,
FormData: FormData,
URLSearchParams: require("url").URLSearchParams
})
Adds a catcher which will be called on every subsequent request error.
Very useful when you need to perform a repetitive action on a specific error code.
const w = wretcher()
.catcher(404, err => redirect("/routes/notfound", err.message))
.catcher(500, err => flashMessage("internal.server.error"))
// No need to catch 404 or 500 code, they are already taken care of.
w.url("http://myapi.com/get/something").get().json(json => /* ... */)
// Default catchers can be overridden if needed.
w.url("...").notFound(err => /* overrides the default 'redirect' catcher */)
Set the fetch options.
wretch("...").options({ credentials: "same-origin" })
Wretch being immutable, you can store the object for later use.
const corsWretch = wretch().options({ credentials: "include", mode: "cors" })
corsWretch.url("http://endpoint1").get()
corsWretch.url("http://endpoint2").get()
// You can mix in with the existing options instead of overriding them by passing a boolean flag :
wretch()
.options({ headers: { "Accept": "application/json" }})
.options({ encoding: "same-origin", headers: { "X-Custom": "Header" }}, true)
/* Options mixed in :
{
headers: { "Accept": "application/json", "X-Custom": "Header" },
encoding: "same-origin"
}
*/
Appends or replaces the url.
wretch({ credentials: "same-origin" }).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.json({ name: "my blog" }).post().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)
Converts a javascript object to query parameters, then appends this query string to the current url.
let w = wretch("http://example.com")
// url is http://example.com
w = w.query({ a: 1, b: 2 })
// url is now http://example.com?a=1&b=2
w = w.query({ c: 3, d: [4, 5] })
// url is now http://example.com?c=3&d=4&d=5
Set request headers.
wretch("...")
.headers({ "Content-Type": "text/plain", Accept: "application/json" })
.body("my text")
.post()
.json()
Shortcut to set the "Accept" header.
wretch("...").accept("application/json")
Shortcut to set the "Content-Type" header.
wretch("...").content("application/json")
A body type is only needed when performing put/patch/post requests with a body.
Set the request body with any content.
wretch("...").body("hello").put()
Sets the content type header, stringifies an object and sets the request body.
const jsonObject = { a: 1, b: 2, c: 3 }
wretch("...").json(jsonObject).post()
Converts the javascript object to a FormData and sets the request body.
const form = {
hello: "world",
duck: "Muscovy"
}
wretch("...").formData(form).post()
Required
You can pass the fetch options here if you prefer.
Perform a get request.
wretch("...").get({ credentials: "same-origin" })
Perform a delete request.
wretch("...").delete({ credentials: "same-origin" })
Perform a put request.
wretch("...").json({...}).put({ credentials: "same-origin" })
Perform a patch request.
wretch("...").json({...}).patch({ credentials: "same-origin" })
Perform a post request.
wretch("...").json({...}).post({ credentials: "same-origin" })
Catchers are optional, but if you do not provide them an error will still be thrown in case of an http error code received.
Catchers can be chained.
type WretcherError = Error & { status: number, response: Response, text?: string, json?: Object }
wretch("...")
.get()
.badRequest(err => console.log(err.status))
.unauthorized(err => console.log(err.status))
.forbidden(err => console.log(err.status))
.notFound(err => console.log(err.status))
.timeout(err => console.log(err.status))
.internalError(err => console.log(err.status))
.error(418, err => console.log(err.status))
.res()
Syntactic sugar for error(400, cb)
.
Syntactic sugar for error(401, cb)
.
Syntactic sugar for error(403, cb)
.
Syntactic sugar for error(404, cb)
.
Syntactic sugar for error(418, cb)
.
Syntactic sugar for error(500, cb)
.
Catch a specific error and perform the callback.
Required
If an error is caught by catchers, the response type handler will not be called.
Raw Response handler.
wretch("...").get().res(response => console.log(response.url))
Json handler.
wretch("...").get().json(json => console.log(Object.keys(json)))
Blob handler.
wretch("...").get().blob(blob => /* ... */)
FormData handler.
wretch("...").get().formData(formData => /* ... */)
ArrayBuffer handler.
wretch("...").get().arrayBuffer(arrayBuffer => /* ... */)
Text handler.
wretch("...").get().text(txt => console.log(txt))
Takes advantage of the Performance API (browsers & node.js) to expose timings related to the request.
Browser timings are very accurate, node.js only contains raw measures.
// Use perfs() before the response types (text, json, ...)
wretch("...")
.get()
.perfs(timings => {
/* Will be called when the timings are ready. */
console.log(timings.startTime)
})
.res()
/* ... */
For node.js, there is a little extra work to do :
// Node.js 8.5+ only
const { performance, PerformanceObserver } = require("perf_hooks")
const fetchPolyfill =
wretch().polyfills({
fetch: function(url, opts) {
performance.mark(url + " - begin")
return fetch(url, opts).then(_ => {
performance.mark(url + " - end")
performance.measure(_.url, url + " - begin", url + " - end")
})
},
/* ... */
performance: performance,
PerformanceObserver: PerformanceObserver
})
MIT