Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Fetch React #1

Merged
merged 19 commits into from
May 3, 2024
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,20 @@ jobs:
run: bun typecheck

- name: Test
run: bun test
run: bun run test
release:
name: release
needs: test
if: ${{ !contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'skip release') && github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- uses: ./.github/setup
- run: bun run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}


9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,11 @@

### ❤️ Contributors

- Bekacru <[email protected]>
- Bekacru <[email protected]>

## v.1.2.0

### 🚀 Release

- typed routes. Now you can create typed routes with betterFetch.
- custom fetch implementation now supports node-fetch
127 changes: 122 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Better Fetch

A fetch wrapper for typescript that returns data and error object. Works on the browser, node (version 18+), workers, deno and bun. Some of the APIs are inspired by [ofetch](https://github.com/unjs/ofetch).
A fetch wrapper for typescript that returns data and error object, supports defined route schemas, plugins and more. Works on the browser, node (version 18+), workers, deno and bun.

## Installation

```bash
pnpm install @better-tools/fetch
```

## Usage
## Basic Usage

```typescript
import fetch from "@better-tools/fetch"
import betterFetch from "@better-tools/fetch"

const { data, error } = await fetch<{
const { data, error } = await betterFetch<{
userId: number;
id: number;
title: string;
Expand Down Expand Up @@ -47,12 +47,52 @@ const { data, error } = await $fetch<{
}>("/todos/1");
```

### ♯ Typed Fetch

Better fetch allows you to define schema that will be used to infer request body, query parameters, response data and error types.

```typescript
import { createFetch } from "@better-tools/fetch";
import { T, FetchSchema } from "@better-tools/fetch/typed";

const routes = {
"/": {
output: T.Object({
message: T.String(),
}),
},
"/signin": {
input: T.Object({
username: T.String(),
password: T.String(),
}),
output: T.Object({
token: T.String(),
}),
},
"/signup": {
input: T.Object({
username: T.String(),
password: T.String(),
optional: T.Optional(T.String()),
}),
output: T.Object({
message: T.String(),
}),
},
} satisfies FetchSchema;

const $fetch = createFetch<typeof routes>()
```


You can also pass default response and error types. Which will be used if you don't pass the types in the fetch call.

```typescript
import { createFetch } from "@better-tools/fetch";
import { DefaultSchema } from "@better-tools/fetch/typed";

const $fetch = createFetch<{
const $fetch = createFetch<DefaultSchema,{
userId: number;
id: number;
title: string;
Expand All @@ -75,6 +115,83 @@ const { data, error } = await $fetch<{
//data and error types are inferred from fetch call
```


### ♯ Using with React
To use better fetch with React hooks, you have the option to import createReactFetch. This allows you to create hooks with custom defaults. Alternatively, you can directly import each individual hook.


With createReactFetch, you can create hooks with custom defaults.
```typescript
import { createReactFetch } from "@better-tools/fetch/react";

//create hooks with custom defaults
const { useFetch, useMutate } = createReactFetch({
baseUrl: "https://jsonplaceholder.typicode.com",
retry: 2,
});

function App() {
type Todo = {
userId: number;
id: number;
title: string;
completed: boolean;
};
const { data, error, isPending } = useFetch<Todo>("/todos/1");
if (error) {
// handle the error
}
if (data) {
// handle the data
}
const { mutate, isPending } = useMutate<Todo>("/todos")
await mutate({
userId: 1,
id: 1,
title: "delectus aut autem",
completed: false
})
}
```

Alternatively, you can directly import each individual hook.
```typescript
import { useFetch, useMutate } from "@better-tools/fetch/react";

function App() {
const { data, error } = useFetch<{
userId: number;
id: number;
title: string;
completed: boolean;
}>("https://jsonplaceholder.typicode.com/todos/1");
if (error) {
// handle the error
}
if (data) {
// handle the data
}
}
```


### ♯ Plugins

Plugins are functions that can be used to modify the request, response, error and other parts of the request lifecycle.

Example:
```typescript
import { createFetch } from "@better-tools/fetch";
import { csrfProtection } from "./plugins/csrfProtection"

const $fetch = createFetch({
baseUrl: "https://jsonplaceholder.typicode.com",
retry: 2,
plugins: [csrfProtection()]
});
```


### ♯ Parsing the response

Better fetch will smartly parse JSON using JSON.parse and if it fails it will return the response as text.
Expand Down
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
},
"suspicious": {
"noExplicitAny": "off"
},
"style": {
"noParameterAssign": "off"
}
}
}
Expand Down
Binary file modified bun.lockb
Binary file not shown.
31 changes: 28 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
{
"name": "@better-tools/fetch",
"version": "1.1.5",
"version": "1.2.0",
"packageManager": "[email protected]",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"react-native": "./dist/index.js",
"types": "./dist/index.d.ts",
"devDependencies": {
"@happy-dom/global-registrator": "^14.7.1",
"@testing-library/react": "^15.0.3",
"@types/bun": "^1.1.0",
"@types/node": "^20.11.30",
"@types/react": "^18.2.79",
"biome": "^0.3.3",
"react": "^18.2.0",
"tsup": "^8.0.2",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"h3": "^1.11.1",
"@sinclair/typebox": "^0.32.27",
"@testing-library/dom": "^10.0.0",
"global-jsdom": "^24.0.0",
"jsdom": "^24.0.0",
"listhen": "^1.7.2",
"mocha": "^10.4.0",
"react-dom": "^18.2.0",
"vitest": "^1.5.0"
},
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./typed": {
"import": "./dist/typed.js",
"require": "./dist/typed.cjs",
"types": "./dist/typed.d.ts"
},
"./react": {
"import": "./dist/react.js",
"require": "./dist/react.cjs",
"types": "./dist/react.d.ts"
}
},
"scripts": {
"build": "tsup",
"test": "vitest run",
"test:watch": "vitest watch",
"lint": "biome check .",
"release": "bun run build && npm publish --access public",
"release": "bun run build && npm publish",
"lint:fix": "biome check . --apply",
"typecheck": "tsc --noEmit"
},
Expand Down
Loading