npm package: @react-solutions/request
Description: A request library for react
Author: React Solutions
Latest version: 1.2.9

# @react-solutions/request

<div align="center">

[![npm version](https://img.shields.io/npm/v/@react-solutions/request.svg?color=blue&style=for-the-badge)](https://www.npmjs.com/package/@react-solutions/request)
[![npm downloads](https://img.shields.io/npm/dm/@react-solutions/request.svg?color=green&style=for-the-badge)](https://www.npmjs.com/package/@react-solutions/request)
[![bundle size](https://img.shields.io/bundlephobia/minzip/@react-solutions/request?color=orange&style=for-the-badge)](https://bundlephobia.com/package/@react-solutions/request)

</div>
A powerful, lightweight React library for making REST and GraphQL API calls with built-in request state management, automatic loading states, error handling, request caching, and TypeScript support.

## 📦 Installation

```bash
npm install @react-solutions/request
```

### Peer Dependencies

This package requires React 16.8+ and axios:

```bash
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:

```typescript
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:

```tsx
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

```tsx
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: "john@example.com" })
			.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

```tsx
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: "jane@example.com" })
			.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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
const [createUser, state] = post("/users", {
	onSuccess: (data) => console.log(data),
	name: "createUser",
});

createUser({ name: "John", email: "john@example.com" })
	.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:**

```typescript
const [updateUser, state] = put("/users/1", {
	onSuccess: (data) => console.log(data),
});

updateUser({ name: "Jane", email: "jane@example.com" })
	.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:**

```typescript
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:**

```typescript
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:**

```typescript
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:**

```typescript
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: "john@example.com" })
	.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:**

```typescript
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:

```typescript
{
  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:**

```tsx
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:**

```tsx
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

```tsx
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: "user@example.com",
		});
	};

	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

```tsx
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

```tsx
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

```typescript
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:

```typescript
{
	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:

```typescript
{
	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:

```typescript
// 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

```typescript
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

- [Axios Documentation](https://axios-http.com/)
- [React Hooks Documentation](https://react.dev/reference/react)

## 💡 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