fetch-decorators
v0.0.7
Published
A set of ES7 composable decorators around the fetch api
Downloads
5
Maintainers
Readme
fetch-decorators
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 afetch
call with your options.@bodify
: prepare passed data (and extra options) into fetch-ready body options.@extractJson
,@extractText,
@extractBlob
: decorates a function returning aResponse
to extract its result as json, text or blob.@extractAuto
: decorates a function returning aResponse
to extract its result automatically based on responseContent-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.