A simple and powerful custom React hook (useApi) that simplifies API requests with automatic loading and error states. Ideal for clean and maintainable data fetching in React components.
npm install @ashraflabs/use-apibash
npm install @ashraflabs/use-api
`
Features
- Simple Hooks: Intuitive useGet, usePost, usePut, and useDelete hooks for REST API operations.
- Debouncing: Prevent excessive API calls with configurable debounce delays.
- Error Handling: Built-in error handling with customizable callbacks.
- FormData Support: Automatically convert data to FormData for file uploads.
- Null/Undefined Protection: Prevent invalid API requests with null or undefined values in URLs.
- TypeScript Support: Fully typed for better developer experience.
Usage
$3
Initialize the useApi hook with your base API URL and headers:
`typescript
import { useApi } from "@ashraflabs/use-api";
export const { useGet, useDelete, usePost, usePut } = useApi(
"http://apibaseurlhere/", // ✅ Base URL
{
headers: {
Authontication: bearer ${localStorage.getItem("token")}, // ✅ Optional headers
},
onRequestDone: (url, endpoint, responseCode) => {
if (endpoint === "/me" && responseCode === 400) {
localStorage.removeItem("token");
router.push("/login");
}
},
}
);
`
$3
Fetch data from an endpoint with optional debouncing and callbacks:
`typescript
const {
data, // Response data
error, // Error object
loading, // Loading state
reload, // Function to refetch data
} = useGet(
/products?query=${searchText}, // Endpoint
{
onDataGet(data) {
// ✅ Success callback
},
onErrorResponse(error) {
// ❌ Error callback
},
debounce: 300, // ⏱ Delay in ms when URL changes, default is 0
preventIncludeNullOrUndefined: true, // 🚫 Skip if URL includes null or undefined
}
);
`
$3
Create resources with support for FormData and error handling:
`typescript
const {
postData, // Function to send POST request
error, // Error object
loading, // Loading state
} = usePost("/products", {
onResponse(res) {
// ✅ Success callback
},
onErrorResponse(error) {
// ❌ Error callback
},
convertToFormData: true, // 🗂️ Automatically convert to FormData
removeIfValueIsNullOrUndefined: true, // 🚫 Prevent invalid requests
});
// Usage
postData({ name: "New Product", price: 100 });
`
$3
Update resources with similar configuration options:
`typescript
const {
putData, // Function to send PUT request
error, // Error object
loading, // Loading state
} = usePut( /products/${productId}, {
onResponse(res) {
// ✅ Success callback
console.log("Product updated:", res);
},
onErrorResponse(error) {
// ❌ Error callback
console.error("Update error:", error);
},
convertToFormData: true, // Convert payload to FormData
removeIfValueIsNullOrUndefined: true, // Skip invalid requests
});
// Usage
putData({ name: "Updated Product", price: 150 });
`
$3
Delete resources with customizable callbacks:
`typescript
const {
deleteData, // Function to send DELETE request
error, // Error object
loading, // Loading state
} = useDelete("/products/1", {
onResponse(res) {
// ✅ Success callback
console.log("Product deleted:", res);
},
onErrorResponse(error) {
// ❌ Error callback
console.error("Deletion error:", error);
},
});
// Usage
deleteData();
`
Configuration Options
Each hook accepts an optional configuration object with the following properties:
| Option | Type | Description |
| -------------------------------- | ---------- | ------------------------------------------------------------ |
| debounce | number | Delay in milliseconds before sending the request (GET only). |
| onDataGet | function | Callback for successful GET response. |
| onResponse | function | Callback for successful POST, PUT, or DELETE response. |
| onErrorResponse | function | Callback for error responses. |
| convertToFormData | boolean | Convert payload to FormData for POST and PUT requests. |
| removeIfValueIsNullOrUndefined | boolean | Prevent requests if URL contains null or undefined. |
| preventIncludeNullOrUndefined | boolean | Prevent GET requests if URL contains null or undefined. |
Example
Below is a complete example demonstrating a product management app:
`javascript
import { useEffect, useState } from "react";
import { useApi } from "@ashraflabs/use-api";
const { useGet, usePost, usePut, useDelete } = useApi(
"http://someapi.base",
{
headers: { Authorization: Bearer ${localStorage.getItem("token")} },
onRequestDone(url, endpoint, responseCode) {
console.log(url, endpoint, responseCode);
if (endpoint === "/me" && responseCode === 400) {
localStorage.removeItem("token");
router.push("/login");
}
},
}
);
type Product = { id: number; name: string; price: number };
type ProductCreationResponse = { message: string };
type ProductUpdationResponse = { message: string };
function App() {
const [searchText, setSearchText] = useState("");
const [debounceTime, setDebounceTime] = useState(0);
const [page, setPage] = useState(1);
useEffect(() => {
setDebounceTime(0);
}, [page]);
// GET: Fetch products
const { data, error, loading, reload } = useGet(
/products?query=${searchText},
{
debounce: debounceTime,
onResponse(data) {
console.log("Products:", data);
},
preventIncludeNullOrUndefined: true,
}
);
// POST: Create product
const { postData, error: createError, loading: creating } = usePost<
Product,
ProductCreationResponse
>("/products", {
convertToFormData: true,
onResponse(res) {
console.log(res.message);
},
});
// PUT: Update product
const { putData, error: updateError, loading: updating } = usePut<
Product,
ProductUpdationResponse
>(/products/1, {
convertToFormData: true,
onResponse(res) {
console.log(res.message);
},
});
// DELETE: Delete product
const { deleteData, error: deleteError, loading: deleting } = useDelete(
"/products",
{
onResponse(res) {
console.log("Deleted");
},
}
);
return (
type="text"
value={searchText}
onChange={(e) => {
setDebounceTime(300);
setSearchText(e.target.value);
}}
/>
);
}
export default App;
``