https://github.com/egamagz/pretch
A lightweight and flexible fetch enhancement library that works with vanilla JavaScript, React, and Preact.
https://github.com/egamagz/pretch
bun deno fetch nodejs preact react vanilla-javascript
Last synced: about 1 month ago
JSON representation
A lightweight and flexible fetch enhancement library that works with vanilla JavaScript, React, and Preact.
- Host: GitHub
- URL: https://github.com/egamagz/pretch
- Owner: EGAMAGZ
- License: mit
- Created: 2024-09-29T06:44:58.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2025-04-09T23:14:34.000Z (about 1 month ago)
- Last Synced: 2025-04-10T00:24:55.352Z (about 1 month ago)
- Topics: bun, deno, fetch, nodejs, preact, react, vanilla-javascript
- Language: TypeScript
- Homepage: https://jsr.io/@pretch
- Size: 312 KB
- Stars: 11
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Pretch
A lightweight and flexible fetch enhancement library that works with vanilla
JavaScript, React, and Preact.[](https://jsr.io/@pretch)

Check the [Documentation](https://jsr.io/@pretch) in JSR
## Features
- 🌐 Universal Compatibility – Seamlessly runs in all JavaScript environments,
including Node.js, Bun, Deno, and browsers.
- ⚛️ Tailored for React & Preact – Enjoy dedicated hooks with full-feature
integration for both frameworks.
- 🔧 Endlessly Customizable – Design custom fetch functions tailored to your
unique requirements.
- 📝 TypeScript Native – Built-in TypeScript support ensures a flawless
developer experience.
- 🛠 Powerful Middleware System – Leverage built-in middleware or create your own
for extended functionality.
- 📦 Web API Driven – Crafted with a focus on modern Web APIs for seamless
integration.## Packages
- `@pretch/core` - Core functionality and middleware system
- `@pretch/react` - React hooks integration
- `@pretch/preact` - Preact hooks integration## Usage
### Core (Vanilla Javascript) - @pretch/core
In the nexts examples, fetch is enhaced with a middleware that will be
automatically add default headers to every request:Create a custom fetch with behaviour enhaced through middleware and a base URL
```ts
import pretch from "@pretch/core";
import { applyMiddleware, defaultHeaders } from "@pretch/core/middleware";const customFetch = pretch(
"https://jsonplaceholder.typicode.com/todos/",
applyMiddleware(
defaultHeaders({
"Content-Type": "application/json; charset=UTF-8",
}, {
strategy: "append",
}),
),
);const getResponse = await customFetch.get("/1");
const createdTodo = await getResponse.json();
// The following request will keep the enhanced behaviour of adding default headers
const putResponse = await customFetch.put("/1", {
body: JSON.stringify({
title: "Updated todo",
body: "Same task",
userId: 1,
}),
});const todoUpdated = await putResponse.json();
```Create a custom fetch with behaviour enhaced through middleware to query
different urls```ts
import pretch from "@pretch/core";
import { applyMiddleware, defaultHeaders } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
defaultHeaders({
"Content-Type": "application/json; charset=UTF-8",
}, {
strategy: "append",
}),
),
);const firstResponse = await customFetch("https://example.com/api/task");
const todo = await firstResponse.json();
const secondResponse = await customFetch(
"https://otherexample.com/api/auth/sing-in",
);const user = await secondResponse.json();
```## Built-in middlewares
Pretch provides a built-in enhancer to apply middlewares on each request
### Validate Status
```ts
import pretch from "@pretch/core";
import { applyMiddleware, validateStatus } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
validateStatus({
validate: (status) => 200 <= status && status <= 399,
errorFactory: (status, request, response) =>
new Error(`Error. Status code: ${status}`),
shouldCancelBody: true,
}),
),
);
```### Retry
```ts
import pretch from "@pretch/core";
import { applyMiddleware, retry } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
retry({
maxRetries: 2,
delay: 1_500,
}),
),
);
```### Default Headers
```ts
import pretch from "@pretch/core";
import { applyMiddleware, defaultHeaders } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
defaultHeaders({
"Content-Type": "application/json; charset=UTF-8",
}, {
strategy: "set", // Optional, by default the headers appended
}),
),
);
```### Authorization
```ts
import pretch from "@pretch/core";
import { applyMiddleware, authorization } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
authorization(
"123456789abcdef",
"bearer",
{
shouldApplyToken: (request: Request) =>
new URL(request.url).pathname.startsWith("/api/"),
},
),
),
);
```### Logging
```ts
import pretch from "@pretch/core";
import {
applyMiddleware,
type ErrorLogData,
logging,
type RequestLogData,
type ResponseLogData,
} from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
logging({
onRequest: async ({ request }: RequestLogData) => {
console.log(`Starting request to ${request.url}`);
},
onResponse: async ({ response }: ResponseLogData) => {
console.log(`Received response with status ${response.status}`);
},
onCatch: async ({ error }: ErrorLogData) => {
console.error(`Request failed:`, error);
},
}),
),
);
```### Proxy
```ts
import pretch from "@pretch/core";
import { applyMiddleware, proxy } from "@pretch/core/middleware";const customFetch = pretch(
applyMiddleware(
proxy(
"/api", // Forward all requests starting with /api
"https://api.example.com",
{
pathRewrite: (path: string) => path.replace(/^\/api/, ""), // Remove /api prefix
},
),
),
);
```## React integration(@pretch/react) and Preact integration(@pretch/preact)
The React and Preact integration delivers powerful hooks for both automatic and
manual fetching, complete with built-in state management. Both packages share a
unified API and identical features, with the only difference being the package
source for importing the hooks.Key Features of the Hooks:
- 🔄 Loading & Error State Management – Effortlessly track request states with
built-in tools.
- 🛡 Type-Safe Response Handling – Enjoy confidence in your data with robust
TypeScript support.
- ⚙️ Request Enhancement – Easily customize and optimize fetch requests to meet
your needs.
- 🛠 Seamless @pretch/core Integration – Fully compatible with middlewares,
enhancers, and other advanced features provided by the @pretch/core package.### Fetching with `useFetch`
`useFetch` automatically fetches the data when the component mounts. You can
refetch the data when the `refetch` function is called.```tsx
import { useFetch } from "@pretch/react";function MyComponent() {
const { data, loading, error, refetch } = useFetch(
"https://api.example.com/todos/1",
);const handleClick = () => {
refetch();
};return (
{loading ? "Loading..." : "Data: " + JSON.stringify(data)}
{error &&Error: {error.message}
}
Refresh
);
}
```### Fetching with `useLazyFetch`
`useLazyFetch` fetches the data manually. You can trigger the fetch with the
`fetchData` function.```tsx
import { useLazyFetch } from "@pretch/react";function MyComponent() {
const { data, loading, error, fetchData } = useLazyFetch({
url: "https://api.example.com/todos/1",
});const handleClick = () => {
fetchData({
newOptions: {
method: "POST",
body: JSON.stringify({ title: "New Todo" }),
},
});
};return (
{loading ? "Loading..." : "Data: " + JSON.stringify(data)}
{error &&Error: {error.message}
}
Create Todo
);
}
```### Fetching with `useQuery`
A hook that provides a set of type-safe HTTP method functions (GET, POST, PUT,
PATCH, DELETE, HEAD, OPTIONS) for making requests to a base URL. It includes
built-in state management using signals to track loading states and errors.```tsx
import { useQuery } from "@pretch/react";interface Todo {
id: number;
title: string;
completed: boolean;
}function TodoExample() {
const { get, post } = useQuery("https://api.example.com");const handleFetch = async () => {
const { data, loading, error } = await get("/todos/1");if (error) {
console.error("Failed to fetch:", error);
return;
}if (data) {
console.log("Todo:", data.title);
}
};const handleCreate = async () => {
const { data, error } = await post("/todos", {
body: JSON.stringify({
title: "New Todo",
completed: false,
}),
});if (data) {
console.log("Created todo:", data);
}
};return (
Fetch Todo
Create Todo
);
}
```The hook provides all standard HTTP methods (`get`, `post`, `put`, `patch`,
`delete`, `head`, `options`) that return a promise containing:- `data`: The parsed response data
- `loading`: Boolean indicating if request is in progress
- `error`: Error object if request failed (or null)Each method supports URL parameters and request options, with full TypeScript
support for response types.### Enhanced the fetching of `useFetch`, `useLazyFetch` and `useQuery`
The hook supports request enhancement through enhancer functions for customizing
request behavior:**Custom enhancer**: Implement your own enhancer function to modify the request
behavior before it is sent.```tsx
import type { Enhancer, Handler } from "@pretch/core";
import { useFetch } from "@pretch/react";function authHeaderEnhancer(handler: Handler) {
return async (request: Request) => {
const modifiedRequest = new Request(request, {
headers: {
...request.headers,
"Authorization": "Bearer my-token",
},
});return handler(modifiedRequest);
};
}function MyComponent() {
const { data } = useFetch("https://example.com", {
enhancer: authHeaderEnhancer,
});return (
Data: {JSON.stringify(data)}
);
}
```**Built-in middlewares**: Otherwise, Pretch provides a built-in enhancer to
apply middlewares on each request, including built-in middlewares.```tsx
import {
applyMiddleware,
authorization,
defaultHeaders,
retry,
} from "@pretch/core";import { useFetch, useLazyFetch, useQuery } from "@precth/react";
function TodoList() {
const { data, loading, error, refetch } = useFetch(
"https://api.example.com/todos/",
{
enhancer: applyMiddleware(
retry({
maxRetries: 2,
delay: 500,
}),
authorization("your-token", "bearer"),
),
},
);const handleClick = () => { refetch() };
return (
{loading ? Loading... : (
{data.map((todo) => {
- {todo.title}
;
})}
)}
{error &&Error: {error.message}
}
Refresh
);
}function CreateTodo() {
const {data, fetchData, error, loading} = useLazyFetch({
url: "https://api.example.com/todos/",
enhancer: applyMiddleware(
defaultHeaders({
"Content-Type": "application/json; charset=UTF-8",
}),
),
});const handleSubmit = async (event: SubmitEvent ) => {
event.preventDefault();
const formData = new FormData(event.target as HTMLFormElement);fetchData({
newOptions: {
method: "POST",
body: JSON.stringify(Object.fromEntries(formData.entries()))
}
})
};return (
Create
);
}
```## Why Pretch?
Struggling to find a simple yet customizable fetching hook library for Preact, I
created @pretch/preact, focusing on ease of use, minimal abstraction, and
alignment with Web APIs. This evolved into @pretch/core, a versatile package for
fetch customization with built-in middlewares and enhancers, later extended to
@pretch/react and @pretch/preact for React and Preact integration.## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Credits
Created by [EGAMAGZ](https://github.com/EGAMAGZ)
## License
MIT License
## TODO
- [x] Create useQuery hook inspired on @tanstack/react-query and redux query
- [ ] Develop and automatize tests for @pretch/preact and @pretch/react