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

react-rest-api

v6.1.0

Published

Use fetch with hooks

Downloads

31

Readme

React-Rest-Api

Release Minified Minified + zip Dependencies Tests Tech Debt Maintainability

Lightweight – no dependencies – React API for rest calls supporting Provider config and use hooks.

It's basically fetch wrapped in a React Context providing React hooks and React Consumer.

So, this doc will not explain how all this concepts works but how this package wrap it.

Features:

  • Configurable: Configure fetch calls, for custom headers or prefix url for example
  • Flexible: Keep full fetch options to be overridable
  • React compliant: Wrap fetch on React context
  • Helper: Automatic resolve query params
  • Safe: Don't mutate the window.fetch

See a working demo on codesandbox : https://codesandbox.io/s/github/HollyPony/react-rest-api-samples

This package is currently served as-is. With usage of all ES6 features without any bundling and/or specific whatever the system is used.

Next features:

  • Proxiing config in order to define a config per method (or whatever the condition)
  • Make unit tests great again

TL;DR – basic full usage sample

npm i react-rest-api

import React, { useState, useEffect, useReducer } from "react";
import ReactDOM from "react-dom";
import { ApiProvider, useApi } from "react-rest-api";

const defaultConfig = {
  headers: {
    // All api calls will take this Content-Type Header
    "Content-Type": "application/json",
    X_MY_APP_API_KEY: "XXX"
  }
};

const App = () => {
  // `url` and `config` refer respectively to `resource` and `init` from https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
  const url = "https://jsonplaceholder.typicode.com";
  const [config, setConfig] = useState(defaultConfig);

  function signin(token) {
    // Update the api config merging the current one
    setConfig({
      ...defaultConfig,
      headers: {
        ...defaultConfig.headers,
        Authorization: token
      }
    });
  }

  // Consider a logout function
  // function signout() {
  //   setConfig(defaultConfig);
  // }

  // All resolved response shoudl be converted to json according to the content type
  function resolveHook(response) {
    // Check the ok prop to consider the response as rejected as needed like:
    return response.ok
      ? // `.json()` if ok
        response.json()
      : // if not `ok` fallback to rejectHook
        Promise.reject(response);
  }

  // Treat fails here before returning to your call
  // Note: The rejected reponse above will fall here
  function rejectHook(response) {
    return Promise.reject(response);
  }

  return (
    <ApiProvider
      url={url} // Optional: prefix url api calls. Litteraly, it's a prefix for api calls.
      config={config} // Optional: Init default config of fetch. It can be overridable per calls later (or wrapping another ApiProvider)
      resolveHook={resolveHook} // Optional: Provide callback function for success fetchs
      rejectHook={rejectHook} // Optionnal: Provider reject callback
    >
      <SignIn signinCallback={signin} />
    </ApiProvider>
  );
};

const SignIn = ({ signinCallback }) => {
  const api = useApi();

  const [dataState, dataDispatch] = useReducer(reducer, {
    status: "initialized",
    payload: {}
  });

  function getSuccess() {
    dataDispatch({ status: "initializing" });
    // Call 'https://myendpoint.co/api/' + '/slug/details' + '?id=42&filter=random'
    api
      .get("/comments", undefined, { id: 42, filter: "random" })
      .then((payload) => {
        // Note that: payload is already jsonified due to resolveCallback
        dataDispatch({ status: "initialized", payload });
      })
      .catch((payload) => {
        // FeelsBadMan
        dataDispatch({ status: "error", payload });
      });
  }

  function postSuccess() {
    dataDispatch({ status: "initializing" });
    api
      .post("/comments", {
        body: JSON.stringify({ prop: "usefull stringify operation" })
      })
      .then((payload) => {
        signinCallback("myToken");
        dataDispatch({ status: "initialized", payload });
      })
      .catch((payload) => {
        dataDispatch({ status: "error", payload });
      });
  }

  function postError() {
    dataDispatch({ status: "initializing" });
    api
      .post("/error", {
        body: JSON.stringify({ prop: "usefull stringify operation" }),
        headers: {
          // This will override the default Content-type. Note the Authorization will be preserved
          // :warning: This is for demo pruposes only, `body: JSON.stringify` should be treated as `application/json`. It's just to show a config override per call.
          "Content-Type": "text/html; charset=utf-8"
        }
      })
      .then((payload) => {
        signinCallback("myToken");
        dataDispatch({ status: "initialized", payload });
      })
      .catch((payload) => {
        dataDispatch({ status: "error", payload });
      });
  }

  return (
    <>
      <div>
        <button onClick={getSuccess}>GET SUCCESS</button>
        <button onClick={postSuccess}>POST SUCCESS</button>
        <button onClick={postError}>POST ERROR</button>
      </div>
      {dataState.status === "initializing" && "loading ..."}
      {dataState.status === "error" && (
        <div>
          Error:
          <pre>{JSON.stringify(dataState.payload, null, 2)}</pre>
        </div>
      )}
      {dataState.status === "initialized" && (
        <div>
          Success:
          <pre>{JSON.stringify(dataState.payload, null, 2)}</pre>
        </div>
      )}
    </>
  );
};

// Wrap the reducer whatever we dont care that's not the point here
function reducer(state, action) {
  return { ...state, ...action };
}

ReactDOM.render(<App />, document.getElementById("root"));

Install

  • npm i react-rest-api

Usage

ApiProvider

Syntax

<ApiProvider
  url={String}
  config={Object}
  resolveHook={Function}
  rejectHook={Function}
/>

url (Optional)

A string that prefix calls. Default to empty string.

Note: There is no extra managment for /. Keep in mind to add one at the end of url or add one before each api call,

config (Optional)

An object gracefully merged for all calls prior to the caller. default to empty Object.

config should be an object corresponding will send as fetch init param by default for all api calls.

Overridable by config param of all api methods.

resolveHook | rejectHook (Optional)

Consider this methods as global fetch wrapper as:

fetch(...)
  .then(resolveHook)
  .catch(rejectHook)

because it's exactly how it's wrapped.

Note: fetch API consider a 400 response as success call with ok: false on then so. See the sample above for a solution. That's my main motivation to write this package.

useApi / apiContext

import { useApi, apiContext } from 'react-rest-api'

Note that useApi is a basic wrapper for useContext(apiContext)

Since it's a configured React context, you have a two native ways to access the apis + convienient hooks provided by this package.

This is the entry point to use the api.

Access api with ApiConsumer

There is no Consumer provided. Feel free to create your local own with the apiContext <apiContext.Consumer /> lessgo.

Context provided

Props Available through the useApi / useContext(apiContext): const api = useApi()

api fetch

api.fetch(url: string, config: Object, queryParams: Object) : Promise

This function is the most important part the main motivation of this project.

  • url: the called url. If one is define in ApiProvider, it will be concatenate. There is currently no transformation arounds the slashes / treatment so don't miss yours ;)

  • config: merge the object with config of ApiProvider.

  • queryParams: is an Object for query params, under the hood use URLSearchParams to build a string then concat to url with a ?.

    api.get('localhost', undefined, {param1: 'value1'}) result to a call on localhost?param1=value1

    queryParams take care of list. { type: [1, 2, 3] } result to ?type=1&type=2&type=3

    Dates object will be parsed with toISOString method. If you wont this behaviour, parse the date before the call, ie. don't pass Date Object.

api get | post | put | del

api.get(url: string, config: Object, queryParams: Object) : Promise
api.post(url: string, config: Object, queryParams: Object) : Promise
api.put(url: string, config: Object, queryParams: Object) : Promise
api.del(url: string, config: Object, queryParams: Object) : Promise

This methods are wrapper for api.fetch with pre-defined config: {method: 'METHOD'} respectively GET, POST, PUT and DELETE

Note: You can call api.get('localhost', method: 'POST'}). This will result to an effective POST call, overriding the method. config on call is always the best.