GithubHelp home page GithubHelp logo

rickwong / fetch-plus Goto Github PK

View Code? Open in Web Editor NEW
118.0 5.0 14.0 643 KB

πŸ• Fetch API with middlewares

License: BSD 3-Clause "New" or "Revised" License

JavaScript 100.00%
nodejs fetch isomorphic fetch-plus json xml rest immutablejs csrf user-agent

fetch-plus's Introduction

Fetch+

A convenient Fetch API library with first-class middleware support.

version license Package Quality installs

Features

  • Drop-in replacement for fetch().
  • Simple BREAD methods for consuming REST APIs.
  • A "queries" object option for building safe query strings more easily.
  • All options can be computed values: myHeaders: () => values
  • Custom middlewares to manipute requests, responses, and caught errors.
  • Useful middlewares and handlers (JSON/Auth/CSRF/Immutable etc) available as separate npm packages.
  • Fetch API Streams draft handler with an Observable interface.
  • Runs in Node and all browsers.

Installation

npm install --save fetch-plus  isomorphic-fetch

Additional middlewares

npm install --save fetch-plus-basicauth
npm install --save fetch-plus-bearerauth
npm install --save fetch-plus-csrf
npm install --save fetch-plus-immutable
npm install --save fetch-plus-json
npm install --save fetch-plus-oauth
npm install --save fetch-plus-stream
npm install --save fetch-plus-useragent
npm install --save fetch-plus-xml

Usage

import/require

import {fetch, createClient} from "fetch-plus";

fetch

fetch("http://some.api.example/v1", {
	query: {foo: "bar"},                // Query string object. So convenient.
	body: () => "R2-D2"                 // Computed values are computed.
});

createClient

Creates a RESTful client so middlewares can be added to it.

const client = createClient("http://some.api.example/v1");

client.addMiddleware

Create middlewares like: (request) => (response) => response

client.addMiddleware(
	(request) => {
		request.path += ".json";
		request.options.headers["Content-Type"] = "application/json; charset=utf-8";

		return (response) => response.json();
	}
);

client.request

request performs generic requests to the configured endpoint.

client.request("posts/25/comments", {
	method: "POST",
	body: {comment: "C-3PO"}
});

client.browse|read|edit|add|destroy|replace

BREAD helpers that perform requests to the configured endpoint.

client.browse(            
	"posts"                    // A string...
);

client.add(
	["posts", 1, "comments"],  // ...or an array like ["posts", id, "comments"]
	{body: "C-3PO"}            // Regular Fetch API body option.
);

client.list|create|read|update|destroy

CRUD aliases that perform requests to the configured endpoint.

client.list(            
	"posts"                    // A string...
);

client.create(
	["posts", 1, "comments"],  // ...or an array like ["posts", id, "comments"]
	{body: "C-3PO"}            // Regular Fetch API body option.
);

handlers

Handlers take configuration and return functions to pass to .then().

// Transform JSON with fetch-plus-json.
import plusJson from "fetch-plus-json";

fetch("http://some.api.example/v1/posts").then(plusJson.handler({some:"config"}));

See example for more.

Community

Let's start one together! After you β˜…Star this project, follow me @Rygu on Twitter.

License

BSD 3-Clause license. Copyright Β© 2015, Rick Wong. All rights reserved.

fetch-plus's People

Contributors

baptistejamin avatar dependabot-preview[bot] avatar dmvdbrugge avatar lukeasrodgers avatar lvgunst avatar mehcode avatar rickwong avatar srolel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

fetch-plus's Issues

Asynchronous request modification in middleware

Currently one can't modify request options asynchronously in middleware.

I think all of the following signatures should be supported as middlewares:
Request β†’ Response β†’ Response
Request β†’ Response β†’ Promise Response
Request β†’ Promise (Response β†’ Response)
Request β†’ Promise (Response β†’ Promise Response)

TypeError: Cannot read property 'headers' of undefined

Running this in node v4.4.1

'use strict';

// import {fetch, createClient} from "fetch-plus"
const dp = require('./lib/debug/dumpPromise');
const isomorphicFetch = require('isomorphic-fetch');
const fetchPlus = require('fetch-plus');
const fetch = fetchPlus.fetch;
const createClient = fetchPlus.createClient;
// import plusJson from "fetch-plus-json";
const plusJson = require('fetch-plus-json');//.plusJson;

const api = createClient("http://www.client1.com");

// Add JSON headers and response transformer.
api.addMiddleware(plusJson);

// Add custom error handler that prints and rethrows any error.
api.addMiddleware((request) => ({error: (e) => {console.warn("Rethrowing: ", e&&e.stack||e); throw e;}}));

function renderJSON (response) {
    console.log("Rendered response to screen");

    console.log(Object.keys(response));
    // console.log(JSON.stringify(response,null,4));
}
function renderError (response) {
    // console.log();

    console.log(JSON.stringify({
        '0' : "Rendered Error response to screen",
        url:response.url,
        status:response.status,
        body:((body)=>{
            if (typeof body === 'string' )
            {
                return body.slice(0,160);
            }
            // return body.read();
            // return body.toString();
            return {
                keys:          Object.keys(body),
                // prototypeKeys: Object.keys(body.prototype)
            };
        })(response.body)
    },null,4));
    console.log(Object.keys(response));
}

const attemptLoginPromise = api.request(["login"], {
    method: "POST",
    body: {
        username: "username",
        password: "password",
    }
});

attemptLoginPromise.then(renderJSON,renderError);
Rethrowing:  TypeError: Cannot read property 'headers' of undefined
    at t.before (/home/forge/www/restful/node_modules/fetch-plus-json/dist/index.js:1:707)
    at /home/forge/www/restful/node_modules/fetch-plus-json/dist/index.js:1:975
    at process._tickCallback (node.js:369:9)

Problems reading json() when status > 400

I do a post to a server and receive a 422 error, the body of the response contains a json object with what validations it failed. However, the docs for fetch indicate that the body of the response hasn't been read at the time of the promise rejection. The body is a ReadableByteStream and the only code I've found to read that is this:

var decoder = new TextDecoder();
var reader = response.body.getReader();

// read() returns a promise that resolves
// when a value has been received
reader.read().then(function processResult(result) {
  if (result.done) return;
  console.log(
    decoder.decode(result.value, {stream: true})
  );

  // Read some more, and recall this function
  return reader.read().then(processResult);
});

The JsonPlus middleware doesn't handle it either. What is the correct way to be able to read the response.json() on a rejected fetch?

Code that may need to change in the fetch-plus source:
http://stackoverflow.com/questions/29473426/fetch-reject-promise-with-json-error-object

Where I found the code to read the body:
https://jakearchibald.com/2016/streams-ftw/

FormData with jsonPlus

Right now I can not pass FormData if I'm using jsonPlus middleware. All my responses comes as json, so I need to pass Accept header as json as well as parse response.
If I have FormData(want to upload file), I don't need Content-Type: application/json header. fetch-plus-json doesn't handle this case.

Workaround I'm using:

client.addMiddleware((request) => {
    if (request.options.body instanceof FormData) {
        request.options.headers["Accept"] = "application/json";
        return plusJson.handler();
    } else {
        return plusJson()(request);
    }
});

adding cookies?

How do you keep a consistent cookiejar with this lib?

for instance:

const client = createClient("http://client1.com")

// add middleware
client.addMiddleware(
    (request) => {
        request.options.headers["Accept"] = "application/json"
        request.options.headers["Content-Type"] = "application/json"
        request.options.headers["X-Requested-With"] = "XMLHttpRequest"
        request.options.credentials = true

        return (res) => res.json()
    }
)

client.request('/login')

// authentication is lost between these two calls

client.request('/users')

Error when getting output as a json object

Hi,
I followed your boilerplate with the githubApi example but in my case, the api returns a json object {...} instead of an array of objects. It gave me a range error. I think fetch-plus expects an array in the output. Is that true?

const _fetchInitialData = () => {
  return myApi.browse(
    ['posts', '1'], {}

Thanks.

Problem with encodeURI with forward-slash in path param

I have a url

/api/namespace/{namespace}?query={query}

namespace path param value is AWS/EC2 (please note forward slash '/' in path param)

Now the issue is:

  1. fetch-plus uses encodeURI (see this line in fetch-plus)

path = normalizeFunc(path.map(compute).map(encodeURI).join("/"));

So this escapes forward-slash, and hence the resulting url from fetch-plus is invalid

/api/namespace/AWS/EC2?query=1

  1. If I use encodeURIComponent for the path param myself before passing to fetch-plus,
    encodeURIComponent('AWS/EC2') = AWS%2FEC2

valid path before fetch-plus:
/api/namespace/AWS%2FEC2?query=1

path converted in fetch-plus:
/api/namespace/AWS%252F?query=1

Notice that %2F is converted to %252F which is wrong. This is because of encodeURI.

So we cannot use fetch-plus in this scenario.

Please take a look.

createClient(url) needs to map CRUD functions

At this moment, createClient only maps BREAD functions like:

endpoint.request = request.bind(null, endpoint);
        endpoint.browse = browse.bind(null, endpoint);
        endpoint.read = read.bind(null, endpoint);
        endpoint.edit = edit.bind(null, endpoint);
        endpoint.replace = replace.bind(null, endpoint);
        endpoint.add = add.bind(null, endpoint);
        endpoint.destroy = destroy.bind(null, endpoint);

We also need to map CRUD functions right?

Query URIEncoded twice

Hi,

Whenever I pass a text query with spaces, the spaces are replaced with %2520 instead of %20, am I doing something wrong here ?

let q = 'test fetch plus'  
endpoint.browse(
      URI,
      {query: {q}}
    )

Request query result : &q=test%2520fetch%2520plus

Implement middleware

middleware looks like this:

// json middleware:
(request) => (response) => response.json()

Issue running fetch-plus-basicauth

I am getting an error when using the fetch-plus-basicauth module.

I create the import:

import plusBasicAuth from "fetch-plus-basicauth";

then I run:

endpoint.addMiddleware(plusBasicAuth("user", 'API_KEY'));

I get the error:

TypeError: btoa() function required but not available

I have tried installing and importing the npm btoa module (https://www.npmjs.com/package/btoa) but this does not seem to resolve the issue.

options.fetch function is called with no arguments

If you supply a fetch function via the options argument, it gets called with no arguments when fetchPlus calls computeObject(options).

Example:

function myFetch(...args) {
  if (args.length === 0) { console.log(" :( "); }
  return fetch(...args);
}

const client = createClient("http://localhost", { fetch: myFetch });
client.get("/foo"); // calls myFetch(), then calls myFetch("http://localhost/foo")

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.