Coder Social home page Coder Social logo

wretch's Introduction

wretch-logo

wretch

travis-badge npm-badge Coverage Status license-badge

A tiny (< 1.6Kb g-zipped) wrapper built around fetch with an intuitive syntax.

f[ETCH] [WR]apper

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.

Table of Contents

Motivation

Because having to write two callbacks for a simple request is awkward.

// 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

wretch("examples/example.json").get().json(json => {
  // Do stuff with the parsed json
})

Because manually checking and throwing every request error code is fastidious.

// 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 */ })

Because sending a json object should be easy.

// 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 => /* ... */)

Because configuration should not rhyme with repetition.

// 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
corsWretch.url("...").get()

// Refine the cors wretch by adding a specific header and a fixed url
const corsWretchWithUrl = corsWretch.url("http://myendpoint.com").headers({ "X-HEADER": "VALUE" })

corsWretchWithUrl.get()
corsWretchWithUrl.json({a:1}).post()

// Reuse the original cors wretch object but this time by adding a baseUrl
const corsWretchWithBaseUrl = corsWretch.baseUrl("http://myurl.com")

corsWretchWithBaseUrl("/root").get()
corsWretchWithBaseUrl("/data/1").get()

Installation

Npm

npm i wretch

Clone

git clone https://github.com/elbywan/wretch
cd wretch
npm install
npm start

Compatibility

Browsers

Wretch is compatible with modern browsers out of the box.

For older environment without fetch support, you should get a polyfill.

Node.js

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
})

Usage

Wretch is bundled using the UMD format (@dist/bundle/wretch.js) alongside es2015 modules (@dist/index.js) and typescript definitions.

Import

<!--- "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")

Code

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(/* ... */)

API


wretch(url = "", opts = {})

Create a new Wretcher object with an url and vanilla fetch options.

Helper Methods

Helper methods are optional and can be chained.

defaults(opts: Object, mixin: boolean = false)

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"
}
*/

errorType(method: "text" | "json" = "text")

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))
  }

polyfills(polyfills: Object)

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
})

catcher(code: number, catcher: (error: WretcherError) => void)

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 */)

options(options: Object, mixin: boolean = false)

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"
}
*/

url(url: string)

Set the url.

wretch({ credentials: "same-origin" }).url("...").get()

baseUrl(baseurl: string)

Returns a wretch factory (same signature as the wretch method) which when called creates a new Wretcher object with the base url as an url prefix.

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

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

query(qp: Object)

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

headers(headerValues: Object)

Set request headers.

wretch("...")
  .headers({ "Content-Type": "text/plain", Accept: "application/json" })
  .body("my text")
  .post()
  .json()

accept(headerValue: string)

Shortcut to set the "Accept" header.

wretch("...").accept("application/json")

content(headerValue: string)

Shortcut to set the "Content-Type" header.

wretch("...").content("application/json")

Body Types

A body type is only needed when performing put/patch/post requests with a body.

body(contents: any)

Set the request body with any content.

wretch("...").body("hello").put()

json(jsObject: Object)

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()

formData(formObject: Object)

Converts the javascript object to a FormData and sets the request body.

const form = {
  hello: "world",
  duck: "Muscovy"
}
wretch("...").formData(form).post()

Http Methods

Required

You can pass the fetch options here if you prefer.

get(opts = {})

Perform a get request.

wretch("...").get({ credentials: "same-origin" })

delete(opts = {})

Perform a delete request.

wretch("...").delete({ credentials: "same-origin" })

put(opts = {})

Perform a put request.

wretch("...").json({...}).put({ credentials: "same-origin" })

patch(opts = {})

Perform a patch request.

wretch("...").json({...}).patch({ credentials: "same-origin" })

post(opts = {})

Perform a post request.

wretch("...").json({...}).post({ credentials: "same-origin" })

Catchers

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()

badRequest(cb: (error: WretcherError) => any)

Syntactic sugar for error(400, cb).

unauthorized(cb: (error: WretcherError) => any)

Syntactic sugar for error(401, cb).

forbidden(cb: (error: WretcherError) => any)

Syntactic sugar for error(403, cb).

notFound(cb: (error: WretcherError) => any)

Syntactic sugar for error(404, cb).

timeout(cb: (error: WretcherError) => any)

Syntactic sugar for error(418, cb).

internalError(cb: (error: WretcherError) => any)

Syntactic sugar for error(500, cb).

error(code: number, cb: (error: WretcherError) => any)

Catch a specific error and perform the callback.

Response Types

Required

If an error is caught by catchers, the response type handler will not be called.

res(cb: (response : Response) => any)

Raw Response handler.

wretch("...").get().res(response => console.log(response.url))

json(cb: (json : Object) => any)

Json handler.

wretch("...").get().json(json => console.log(Object.keys(json)))

blob(cb: (blob : Blob) => any)

Blob handler.

wretch("...").get().blob(blob => /* ... */)

formData(cb: (fd : FormData) => any)

FormData handler.

wretch("...").get().formData(formData => /* ... */)

arrayBuffer(cb: (ab : ArrayBuffer) => any)

ArrayBuffer handler.

wretch("...").get().arrayBuffer(arrayBuffer => /* ... */)

text(cb: (text : string) => any)

Text handler.

wretch("...").get().text(txt => console.log(txt))

Performance API (experimental)

perfs(cb: (timings: PerformanceTiming) => void)

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
})

License

MIT

wretch's People

Contributors

bb010g avatar elbywan avatar juliuxu avatar

Watchers

 avatar  avatar

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.