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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@react-solutions/request

v1.2.0

Published

A request library for react

Readme

@react-solutions/request

npm version npm downloads bundle size

📦 Installation

npm install @react-solutions/request

Peer Dependencies

This package requires React 16.8+ and axios:

npm install react react-dom axios

🚀 Features

  • REST API Support - GET, POST, PUT, PATCH, DELETE methods
  • GraphQL Support - Query and Mutation operations
  • Built-in State Management - Automatic loading and error states using React hooks
  • Request Caching - Store and retrieve request data by name
  • Refetch Support - Easy refetching for GET and GraphQL queries
  • TypeScript Support - Full TypeScript definitions included
  • Request/Response Interceptors - Customize requests and responses
  • Authorization Token Management - Easy authentication handling
  • Configurable Base URL - Set and update base URLs dynamically
  • Session Storage - Automatic persistence of configuration and request data
  • Error Handling - Comprehensive error processing with detailed error objects
  • Callback Support - onSuccess and onError callbacks for each request

📚 Quick Start

1. Basic Setup

First, configure your request instance:

import { createConfig } from "@react-solutions/request";

const requestInstance = createConfig({
	baseURL: "https://api.example.com",
	config: {
		headers: {
			"Content-Type": "application/json",
			Accept: "application/json",
		},
		timeout: 5000,
	},
});

export default requestInstance;

2. Using Request Provider (Optional)

Wrap your app with RequestProvider to use a custom axios instance:

import { RequestProvider } from "@react-solutions/request";
import requestInstance from "./config";

function App() {
	return (
		<RequestProvider client={requestInstance}>
			<YourComponents />
		</RequestProvider>
	);
}

Note: The RequestProvider uses the client prop.

3. Making REST API Calls

import { get, post, put, patch, del } from "@react-solutions/request";

function MyComponent() {
	// GET Request (returns [requestFunction, state, refetchFunction])
	const [getUsers, getState, refetchUsers] = get("/users", {
		onSuccess: (data, payload) => {
			console.log("Success:", data);
		},
		onError: (error, payload) => {
			console.log("Error:", error);
		},
		name: "getUsers", // Optional: name for caching/retrieval
	});

	// POST Request (returns [requestFunction, state])
	const [createUser, postState] = post("/users", {
		onSuccess: (data, payload) => {
			console.log("User created:", data);
		},
		name: "createUser", // Optional: name for caching
	});

	const handleGetUsers = () => {
		getUsers({ page: 1, limit: 10 })
			.then((res) => console.log("Response:", res))
			.catch((err) => console.log("Error:", err));
	};

	const handleRefetch = () => {
		// Refetch with same parameters
		refetchUsers()
			.then((res) => console.log("Refetched:", res))
			.catch((err) => console.log("Error:", err));
	};

	const handleCreateUser = () => {
		createUser({ name: "John Doe", email: "[email protected]" })
			.then((res) => console.log("Created:", res))
			.catch((err) => console.log("Error:", err));
	};

	return (
		<div>
			<button onClick={handleGetUsers}>Get Users</button>
			<button onClick={handleRefetch}>Refetch Users</button>
			<button onClick={handleCreateUser}>Create User</button>
			{getState.isLoading && <p>Loading...</p>}
			{getState.error && <p>Error: {getState.error?.message}</p>}
			{getState.data && <pre>{JSON.stringify(getState.data, null, 2)}</pre>}
		</div>
	);
}

4. Making GraphQL Calls

import { gqlQuery, gqlMutation } from "@react-solutions/request";

const GET_USERS = `
  query GetUsers($limit: Int) {
    users(limit: $limit) {
      id
      name
      email
    }
  }
`;

const CREATE_USER = `
  mutation CreateUser($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
      email
    }
  }
`;

function GraphQLComponent() {
	// GraphQL Query (returns [requestFunction, state, refetchFunction])
	const [getUsers, queryState, refetchUsers] = gqlQuery(GET_USERS, {
		onSuccess: (data) => console.log("Query result:", data),
		onError: (error) => console.log("Query error:", error),
		name: "getUsers", // Optional: name for caching
	});

	// GraphQL Mutation (returns [requestFunction, state])
	const [createUser, mutationState] = gqlMutation(CREATE_USER, {
		onSuccess: (data) => console.log("Mutation result:", data),
		name: "createUser", // Optional: name for caching
	});

	const handleQuery = () => {
		getUsers({ limit: 10 })
			.then((res) => console.log("Users:", res))
			.catch((err) => console.log("Error:", err));
	};

	const handleMutation = () => {
		createUser({ name: "Jane Doe", email: "[email protected]" })
			.then((res) => console.log("Created:", res))
			.catch((err) => console.log("Error:", err));
	};

	return (
		<div>
			<button onClick={handleQuery}>Get Users</button>
			<button onClick={handleMutation}>Create User</button>
			{queryState.isLoading && <p>Loading...</p>}
			{queryState.error && <p>Error: {queryState.error?.message}</p>}
			{queryState.data && <pre>{JSON.stringify(queryState.data, null, 2)}</pre>}
		</div>
	);
}

📖 API Reference

Configuration Methods

createConfig(options)

Creates a new request instance with the specified configuration. The configuration is automatically persisted.

Parameters:

  • baseURL (string, required): Base URL for all requests
  • config (RequestConfig, optional): Additional configuration

Returns: RequestInstance (RequestInstance)

Example:

import { createConfig } from "@react-solutions/request";

const instance = createConfig({
	baseURL: "https://api.example.com",
	config: {
		headers: {
			Authorization: "Bearer token123",
		},
		timeout: 5000,
	},
});

updateConfig(options)

Updates the existing request configuration and recreates the request instance. The updated configuration.

Parameters:

  • baseURL (string, optional): New base URL (uses existing if not provided)
  • config (RequestConfig, optional): Updated configuration (merges with existing)

Example:

import { updateConfig } from "@react-solutions/request";

// Update base URL
updateConfig({
	baseURL: "https://new-api.example.com",
});

// Update configuration
updateConfig({
	baseURL: "https://api.example.com",
	config: {
		headers: {
			"X-Custom-Header": "value",
		},
	},
});

setRequestInstance(requestInstance)

Sets a custom request instance (RequestInstance) to be used by all request methods.

Parameters:

  • requestInstance (RequestInstance): Custom instance

Example:

import { setRequestInstance } from "@react-solutions/request";
import axios from "axios";

const customInstance = axios.create({
	baseURL: "https://api.example.com",
	timeout: 10000,
});

setRequestInstance(customInstance);

Authorization

authorizationToken(token)

Sets the authorization token for all requests. The token is automatically added to the Authorization header and persisted.

Parameters:

  • token (string): Authorization token (with or without "Bearer" prefix)

Example:

import { authorizationToken } from "@react-solutions/request";

// Token will be automatically added as Authorization header
authorizationToken("Bearer your-token-here");
// or
authorizationToken("your-token-here"); // Bearer prefix is optional

Interceptors

requestInterceptor(onFulfilled, onRejected?)

Adds a request interceptor to modify requests before they are sent.

Parameters:

  • onFulfilled (function): Callback when request is successful - receives config object, must return config
  • onRejected (function, optional): Callback when request fails - receives error object

Example:

import { requestInterceptor } from "@react-solutions/request";

requestInterceptor(
	(config) => {
		// Modify request config
		config.headers["X-Custom-Header"] = "value";
		return config;
	},
	(error) => {
		// Handle request error
		return Promise.reject(error);
	}
);

responseInterceptor(onFulfilled, onRejected?)

Adds a response interceptor to modify responses or handle errors.

Parameters:

  • onFulfilled (function): Callback when response is successful - receives response object, must return response
  • onRejected (function, optional): Callback when response fails - receives error object

Example:

import { responseInterceptor } from "@react-solutions/request";

responseInterceptor(
	(response) => {
		// Modify response
		return response;
	},
	(error) => {
		// Handle response error (e.g., token expiry)
		if (error.response?.status === 401) {
			// Redirect to login
		}
		return Promise.reject(error);
	}
);

REST API Methods

All REST methods, return an array containing the request function and state.

get(url, props)

Creates a GET request hook. Returns 3 items: [requestFunction, state, refetchFunction].

Parameters:

  • url (string): API endpoint (relative to baseURL if configured)
  • props (object, optional):
    • onSuccess (function, optional): Success callback (data, payload) => any - return value will be stored as data
    • onError (function, optional): Error callback (error, payload) => any - return value will be stored as error
    • name (string, optional): Name for storing request data (enables caching and retrieval)

Returns: [getRequest, state, refetchRequest]

  • getRequest(payload?, config?): Function to execute the request
    • payload (any, optional): Query parameters object
    • config (RequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse>
  • state: Request state object
  • refetchRequest(): Function to refetch with the same parameters (only available if name was provided)

Example:

const [getUsers, state, refetchUsers] = get("/users", {
	onSuccess: (data) => {
		console.log(data);
		// Optionally transform data
		return data.map((user) => ({ ...user, displayName: user.name }));
	},
	onError: (error) => console.error(error),
	name: "getUsers", // Required for refetch
});

// Execute request with query parameters
getUsers({ page: 1, limit: 10 }, { timeout: 5000 })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

// Refetch with same parameters
refetchUsers()
	.then((res) => console.log("Refetched:", res))
	.catch((err) => console.error(err));

post(url, props)

Creates a POST request hook. Returns 2 items: [requestFunction, state].

Parameters:

  • url (string): API endpoint
  • props (object, optional): Same as get

Returns: [postRequest, state]

  • postRequest(payload?, config?): Function to execute the request
    • payload (any, optional): Request body
    • config (AxiosRequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse>

Example:

const [createUser, state] = post("/users", {
	onSuccess: (data) => console.log(data),
	name: "createUser",
});

createUser({ name: "John", email: "[email protected]" })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

put(url, props)

Creates a PUT request hook for updating resources. Returns 2 items: [requestFunction, state].

Parameters:

  • url (string): API endpoint
  • props (object, optional): Same as get

Returns: [putRequest, state]

  • putRequest(payload?, config?): Function to execute the request
    • payload (any, optional): Request body
    • config (AxiosRequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse>

Example:

const [updateUser, state] = put("/users/1", {
	onSuccess: (data) => console.log(data),
});

updateUser({ name: "Jane", email: "[email protected]" })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

patch(url, props)

Creates a PATCH request hook for partial updates. Returns 2 items: [requestFunction, state].

Parameters:

  • url (string): API endpoint
  • props (object, optional): Same as get

Returns: [patchRequest, state]

  • patchRequest(payload?, config?): Function to execute the request
    • payload (any, optional): Request body
    • config (AxiosRequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse>

Example:

const [patchUser, state] = patch("/users/1", {
	onSuccess: (data) => console.log(data),
});

patchUser({ name: "Updated Name" })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

del(url, props)

Creates a DELETE request hook. Returns 2 items: [requestFunction, state].

Parameters:

  • url (string): API endpoint
  • props (object, optional): Same as get

Returns: [deleteRequest, state]

  • deleteRequest(config?): Function to execute the request
    • config (AxiosRequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse>

Example:

const [deleteUser, state] = del("/users/1", {
	onSuccess: (data) => console.log(data),
});

deleteUser({ timeout: 5000 })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

GraphQL Methods

gqlQuery(query, props)

Creates a GraphQL query hook. Sends POST request to /graphql endpoint. Returns 3 items: [requestFunction, state, refetchFunction].

Parameters:

  • query (string): GraphQL query string
  • props (object, optional):
    • onSuccess (function, optional): Success callback (data, payload) => any
    • onError (function, optional): Error callback (error, payload) => any
    • name (string, optional): Name for storing request data (enables caching and retrieval)

Returns: [queryRequest, state, refetchRequest]

  • queryRequest(variables?, config?): Function to execute the query
    • variables (any, optional): GraphQL variables object
    • config (RequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse> (response.data contains the GraphQL data)
  • state: Request state object
  • refetchRequest(): Function to refetch with the same variables (only available if name was provided)

Example:

const GET_USERS = `
  query GetUsers($limit: Int) {
    users(limit: $limit) {
      id
      name
    }
  }
`;

const [getUsers, state, refetchUsers] = gqlQuery(GET_USERS, {
	onSuccess: (data) => console.log(data),
	name: "getUsers", // Required for refetch
});

getUsers({ limit: 10 })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

// Refetch with same variables
refetchUsers()
	.then((res) => console.log("Refetched:", res))
	.catch((err) => console.error(err));

gqlMutation(mutation, props)

Creates a GraphQL mutation hook. Sends POST request to /graphql endpoint. Returns 2 items: [requestFunction, state].

Parameters:

  • mutation (string): GraphQL mutation string
  • props (object, optional): Same as gqlQuery

Returns: [mutationRequest, state]

  • mutationRequest(variables?, config?): Function to execute the mutation
    • variables (any, optional): GraphQL variables object
    • config (RequestConfig, optional): Additional request config
    • Returns: Promise<SuccessResponse> (response.data contains the GraphQL data)

Example:

const CREATE_USER = `
  mutation CreateUser($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
      email
    }
  }
`;

const [createUser, state] = gqlMutation(CREATE_USER, {
	onSuccess: (data) => console.log(data),
});

createUser({ name: "John", email: "[email protected]" })
	.then((res) => console.log(res))
	.catch((err) => console.error(err));

Request Retrieval

retrieve(name, callback?)

Retrieves the response data from a previously made request by name. Useful for accessing cached request data without making a new request.

Parameters:

  • name (string): The name of the stored request (must match the name prop used when creating the request)
  • callback (function, optional): Optional callback to process the response before returning (res) => any

Returns: The response data from the stored request, or null if not found

Example:

import { retrieve } from "@react-solutions/request";

// Get stored request data
const userData = retrieve("getUsers");

// Get and transform stored request data
const transformedData = retrieve("getUsers", (data) => {
	return data.map((user) => ({ ...user, displayName: user.name }));
});

State Object

Each request method returns a state object with the following properties:

{
  isLoading: boolean;    // True when request is in progress
  error: {               // Error object (null initially)
    status: number;
    message: string;
    data: any;
    statusText: string;
    code: string;
    name: string;
  } | null;
  data: any;             // Response data (null initially)
}

Note: The state object does not include isSuccess or isError boolean flags. Instead, check state.data !== null for success and state.error !== null for errors.

RequestProvider Component

The RequestProvider component allows you to use a custom request instance throughout your app.

Props:

  • client (RequestInstance): Custom axios instance
  • children (ReactNode): Child components

Example:

import { RequestProvider } from "@react-solutions/request";
import axios from "axios";

const customInstance = axios.create({
	baseURL: "https://api.example.com",
	headers: {
		"X-Custom-Header": "value",
	},
});

function App() {
	return (
		<RequestProvider client={customInstance}>
			<YourComponents />
		</RequestProvider>
	);
}

useRequest Hook

The useRequest hook provides access to the request instance from the RequestProvider context.

Returns: The request instance (AxiosInstance) from the context

Example:

import { useRequest } from "@react-solutions/request";

function MyComponent() {
	const requestInstance = useRequest();

	// Use the instance directly if needed
	requestInstance.get("/custom-endpoint").then((res) => console.log(res));
}

🎯 Usage Examples

Complete REST API Example

import { get, post, updateConfig, authorizationToken } from "@react-solutions/request";
import { useEffect } from "react";

function UsersComponent() {
	const [getUsers, getState, refetchUsers] = get("/users", {
		onSuccess: (data) => {
			console.log("Users loaded:", data);
		},
		onError: (error) => {
			console.error("Failed to load users:", error);
		},
		name: "getUsers", // Enable caching and refetch
	});

	const [createUser, postState] = post("/users", {
		onSuccess: (data) => {
			console.log("User created:", data);
			// Refresh users list
			refetchUsers();
		},
		name: "createUser",
	});

	useEffect(() => {
		// Configure base URL on mount
		updateConfig({
			baseURL: "https://jsonplaceholder.typicode.com",
		});

		// Set auth token if available
		const token = localStorage.getItem("authToken");
		if (token) {
			authorizationToken(token);
		}

		// Load users on mount
		getUsers({ _limit: 10 });
	}, []);

	const handleCreateUser = () => {
		createUser({
			name: "New User",
			email: "[email protected]",
		});
	};

	return (
		<div>
			<button
				onClick={handleCreateUser}
				disabled={postState.isLoading}
			>
				{postState.isLoading ? "Creating..." : "Create User"}
			</button>

			<button onClick={() => refetchUsers()}>Refresh</button>

			{getState.isLoading && <p>Loading users...</p>}
			{getState.error && <p>Error: {getState.error?.message}</p>}
			{getState.data && (
				<ul>
					{getState.data.map((user: any) => (
						<li key={user.id}>{user.name}</li>
					))}
				</ul>
			)}
		</div>
	);
}

Complete GraphQL Example

import { gqlQuery, gqlMutation, updateConfig } from "@react-solutions/request";
import { useEffect } from "react";

const GET_POSTS = `
  query GetPosts($limit: Int) {
    posts(limit: $limit) {
      id
      title
      body
    }
  }
`;

const CREATE_POST = `
  mutation CreatePost($title: String!, $body: String!) {
    createPost(title: $title, body: $body) {
      id
      title
      body
    }
  }
`;

function PostsComponent() {
	// Set GraphQL endpoint
	useEffect(() => {
		updateConfig({
			baseURL: "https://api.example.com",
		});
	}, []);

	const [getPosts, queryState, refetchPosts] = gqlQuery(GET_POSTS, {
		onSuccess: (data) => console.log("Posts:", data),
		onError: (error) => console.error("Query error:", error),
		name: "getPosts", // Enable caching and refetch
	});

	const [createPost, mutationState] = gqlMutation(CREATE_POST, {
		onSuccess: (data) => {
			console.log("Post created:", data);
			refetchPosts(); // Refresh list
		},
		name: "createPost",
	});

	const handleGetPosts = () => {
		getPosts({ limit: 10 });
	};

	const handleCreatePost = () => {
		createPost({
			title: "New Post",
			body: "Post content here",
		});
	};

	return (
		<div>
			<button onClick={handleGetPosts}>Get Posts</button>
			<button
				onClick={handleCreatePost}
				disabled={mutationState.isLoading}
			>
				Create Post
			</button>

			{queryState.isLoading && <p>Loading...</p>}
			{queryState.error && <p>Error: {queryState.error?.message}</p>}
			{queryState.data && (
				<div>
					{queryState.data.posts?.map((post: any) => (
						<div key={post.id}>
							<h3>{post.title}</h3>
							<p>{post.body}</p>
						</div>
					))}
				</div>
			)}
		</div>
	);
}

Using Request Caching and Retrieval

import { get, retrieve } from "@react-solutions/request";

function UserProfileComponent() {
	const [getUser, userState] = get("/users/1", {
		name: "getUserProfile", // Store with name
		onSuccess: (data) => console.log("User loaded:", data),
	});

	useEffect(() => {
		getUser();
	}, []);

	// In another component or function, retrieve cached data
	const handleShowCachedData = () => {
		const cachedUser = retrieve("getUserProfile");
		if (cachedUser) {
			console.log("Cached user:", cachedUser);
		}
	};

	// Transform cached data
	const handleShowTransformedData = () => {
		const transformedUser = retrieve("getUserProfile", (data) => ({
			...data,
			fullName: `${data.firstName} ${data.lastName}`,
		}));
		console.log("Transformed user:", transformedUser);
	};

	return (
		<div>
			{userState.isLoading && <p>Loading...</p>}
			{userState.data && <pre>{JSON.stringify(userState.data, null, 2)}</pre>}
			<button onClick={handleShowCachedData}>Show Cached Data</button>
			<button onClick={handleShowTransformedData}>Show Transformed Data</button>
		</div>
	);
}

Using Interceptors for Global Error Handling

import { requestInterceptor, responseInterceptor, updateConfig } from "@react-solutions/request";

// Setup global interceptors
requestInterceptor((config) => {
	// Add timestamp to all requests
	config.headers["X-Request-Time"] = Date.now().toString();
	return config;
});

responseInterceptor(
	(response) => {
		// Log successful responses
		console.log("Response:", response);
		return response;
	},
	(error) => {
		// Handle global errors
		if (error.response?.status === 401) {
			// Token expired, redirect to login
			window.location.href = "/login";
		} else if (error.response?.status === 403) {
			// Forbidden
			console.error("Access forbidden");
		} else if (error.response?.status >= 500) {
			// Server error
			console.error("Server error:", error);
		}
		return Promise.reject(error);
	}
);

// Configure base URL
updateConfig({
	baseURL: "https://api.example.com",
});

🔧 Error Handling

Errors are automatically processed and include the following structure:

{
	status: number; // HTTP status code (default: 500)
	message: string; // Error message from response or fallback
	data: any; // Full error response data
	statusText: string; // HTTP status text
	code: string; // Error code
	name: string; // Error name
}

Error messages are extracted in the following priority:

  1. response.data.detail
  2. response.data.message
  3. error.message
  4. Default: "An unknown error occurred"

For GraphQL errors, the first error message from the errors array is used.

Success Response Structure

Successful requests return a promise that resolves to:

{
	data: any; // Response data (or transformed data from onSuccess callback)
	status: number; // HTTP status code
	statusText: string; // HTTP status text
	payload: any; // Original payload/variables sent with the request
}

💾 Session Storage

The library automatically persists the following to session storage:

  • Base URL configuration
  • Request configuration
  • Authorization token
  • Request data (when name prop is provided)

This allows the configuration to persist across page refreshes within the same browser session. To clear the stored data:

// The library doesn't expose a clear method, but you can manually clear:
sessionStorage.removeItem("requestManagerState");

📝 TypeScript Support

This package is written in TypeScript and includes full type definitions. All exported functions and hooks are fully typed.

Type Definitions

import type {
	RequestState,
	ErrorResponse,
	RequestProps,
	SuccessResponse,
} from "@react-solutions/request";

// Use in your components
const [getUsers, state] = get<{ id: number; name: string }[]>("/users");
// state.data will be typed as { id: number; name: string }[] | null

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT

🔗 Related

💡 Tips

  1. Always configure baseURL: Use updateConfig or createConfig to set your base URL
  2. Use interceptors for global logic: Add authentication tokens, logging, or error handling
  3. Leverage state objects: Use isLoading, error, and data for UI state management
  4. Handle errors properly: Use both onError callbacks and .catch() for comprehensive error handling
  5. GraphQL errors: GraphQL errors are automatically converted to HTTP-like error objects for consistent handling
  6. Use names for caching: Provide a name prop to enable request caching and refetch functionality
  7. Refetch support: GET and GraphQL queries support refetching when a name is provided
  8. Session persistence: Configuration and request data are automatically saved to session storage

Made with ❤️ for React developers