npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fetch-decorators

v0.0.7

Published

A set of ES7 composable decorators around the fetch api

Downloads

5

Readme

fetch-decorators

npm package Build Status Commitizen friendly Code Climate Test Coverage npm package

A set of composable ES7 decorators around the fetch api

Automate things request and response body parsing so you don't have to.

No dependency (oh, except a fetch polyfill maybe)

Usage TL;DR:

npm i -S fetch-decorators
class Messages {
  @extractJson
  @bodify
  @fetchify({method: 'POST'})
  post(userId) {
    return `/users/${userId}/messages`;
  }
}

const messages = new Messages();

messages.post('ArnaudRinquin')({
  content: 'Hello World',
  public: true,
  draft: false,
}).then(({response, data}) => {
  // response === the original fetch response
  // data === the JSON object returned by the server
});

Decorators

  • @fetchify: decorates a function returning a url to a fetch call with your options.
  • @bodify: prepare passed data (and extra options) into fetch-ready body options.
  • @extractJson, @extractText, @extractBlob: decorates a function returning a Response to extract its result as json, text or blob.
  • @extractAuto: decorates a function returning a Response to extract its result automatically based on response Content-Type header.

These decorators have been designed to be composable, give it a try!

@fetchify(options:?object)

This helper wraps the original function into a fetch call so it may just return a string, and then be called with optional data, headers, options.

(originalArgs) => url:string

becomes:

(originalArgs) => (options:?object) => fetchResponse:promise

import { fetchify } from 'fetch-decorators';

class Users {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }

  @fetchify()
  get(userId) {
    return `${this.baseUrl}/users/${userId}`;
  }

  @fetchify({
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    }
  })
  create() {
    return `${this.baseUrl}/users`;
  }
}

const userApi = new UserApi('/api');

userApi.createUser()({
  body: JSON.stringify({
    firstName: 'Walter',
    lastName: 'White',
  }
})).then(function(response){
  // Regular `fetch` response
);

userApi.getUser('fakeUserId123')().then(function(response){
  // Regular `fetch` response
});

@bodify

Takes body data and options and calls the decorated function with a proper fetch options object where options.body is passed data as a string.

(options) => orignalResult

becomes:

(originalArgs) => (data:object, extraOptions:?object) => orignalResult(options)

import { bodify } from 'fetch-decorators';

class Messages {
  @bodify
  create(userId) {
    return function(options) {
      return fetch(`/api/users/${userId}/messages`, options);
    };
  }
}

const messages = new Messages();
const messages = {
  content: 'Hello',
  draft: false,
};
const options = { method: 'POST' };

users.create('fakeUserId')(message, options).then(function(response){
  // response === the original fetch response
});

@extractJson, @extractText, @extractBlob

These decorators wrap functions returning a fetch promise with the matching Response extraction function.

(originalArgs) => fetchResponse:promise

becomes:

(originalArgs) => (options:?object) => extractionResult:promise

where the extractionResult promise resolves with : {response:Response, data:any}

import { extractJson } from 'fetch-decorators';

class Users {
  @extractJson
  get(userId) {
    return (options) => fetch(`/api/users/${userId}`, options);
  }
}

const users = new Users();

users.get('userId123')().then(function({response, data}){
  // response === the original fetch response
  // data === the extracted data, here a `user` JSON object
});

@extractAuto

This extractor has the same signature and behaviour as other extractors but will use the Reponse Content-Type header to determine the right Response method to use for data extraction.

Content types are matched this way:

'application/json': 'json'
'text/plain': 'text'
'default': 'blob'

Composition

All these decorators where designed so it's easy to use them together, just stack them! Note: obviously, the order matters.

import {
  fetchify,
  bodify,
  extractJson,
} from 'fetch-decorators';

class Messages {
  @extractJson
  @bodify
  @fetchify({method: 'POST'})
  post(userId) {
    return `/users/${userId}/messages`;
  }

  // Approximate equivalent without decorators
  // Thanks to ES6, the volume of code is roughly the same
  // But the complexity is higher and you'll probably
  // have a lot of code duplication
  mehPost(userId, data, extraOptions) {
    return fetch(`/users/${userId}/messages`, {
      method: 'POST',
      ...extraOptions,
      body: JSON.stringify(data),
    }).then((response) => response.json());
  }
}

const messagesApi = new Messages();

// Request body, as an object
const message = {
  content: 'Hello World',
  public: true,
  draft: false,
};

// Some extra options
const authHeaders = {
  headers: {
    'X-AUTH-MUCH-SECURE': '123FOO456BAR',
  },
};

messagesApi.post('ArnaudRinquin')(message, authHeaders).then(({response, data}) => {
  // response === the original fetch response
  // data === the JSON object returned by the server
});

FAQ

Is it just syntactic sugar?

Yes.

I have a more complex case where my [ headers are dynamic | I have to change fetch options | whatever ]. How do I do?

Then you should manually write your call to fetch instead of shoehorning these decorators in your code.