Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions doc/content/docs/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Hooks
description: Hooks
---

Hooks are functions that are called at different stages of the request lifecycle.
Hooks are functions that are called at different stages of the request lifecycle.

```ts twoslash title="fetch.ts"
import { createFetch } from "@better-fetch/fetch";
Expand Down Expand Up @@ -50,7 +50,7 @@ import { createFetch } from "@better-fetch/fetch";

const $fetch = createFetch({
baseURL: "http://localhost:3000",
onResponse(context) {
onResponse(context) {
// do something with the context
return context.response // return the response
},
Expand All @@ -61,6 +61,7 @@ const $fetch = createFetch({
## On Success and On Error

on success and on error are callbacks that will be called when a request is successful or when an error occurs. The function will be called with the response context as an argument and it's not expeceted to return anything.
If schema validation fails, the `onError` hook will be called as well.

```ts twoslash title="fetch.ts"
import { createFetch } from "@better-fetch/fetch";
Expand All @@ -72,6 +73,45 @@ const $fetch = createFetch({
},
onError(context) {
// do something with the context
// console.log(context.error.type); // "validation" | "http"
},
})
```
```

### Error Types

The `onError` hook receives different error types depending on what caused the error:

#### HTTP Errors
When an HTTP request fails (non-2xx status codes), the error context contains:

```ts
{
response: Response,
request: RequestContext,
error: {
type: "http",
status: number,
statusText: string,
// ... other response data
},
responseText: string
}
```

#### Validation Errors
When schema validation fails on the response data, the error context contains:

```ts
{
response: Response,
request: RequestContext,
error: {
type: "validation",
issues: Array<ValidationIssue>,
message: string,
status: number,
statusText: string,
}
}
```
41 changes: 37 additions & 4 deletions packages/better-fetch/src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isJSONParsable,
jsonParse,
parseStandardSchema,
ValidationError,
} from "./utils";

export const betterFetch = async <
Expand Down Expand Up @@ -129,10 +130,41 @@ export const betterFetch = async <
*/
if (context?.output) {
if (context.output && !context.disableValidation) {
successContext.data = await parseStandardSchema(
context.output as StandardSchemaV1,
successContext.data,
);
try {
successContext.data = await parseStandardSchema(
context.output as StandardSchemaV1,
successContext.data,
);
} catch (err) {
const validationError = err as ValidationError;

// Create validation error context for hooks
const validationErrorContext = {
response: response ?? Response.json({ error: validationError.message }),
request: context,
error: {
type: 'validation' as const,
issues: validationError.issues,
message: validationError.message,
status: response.status,
statusText: response.statusText,
},
};


if (options?.onError) {
await options.onError(validationErrorContext);
}

// Call all onError hooks with validation error
for (const onError of hooks.onError) {
if (onError) {
await onError(validationErrorContext);
}
}

throw validationError;
}
}
}

Expand Down Expand Up @@ -169,6 +201,7 @@ export const betterFetch = async <
request: context,
error: {
...errorObject,
type: 'http' as const,
status: response.status,
statusText: response.statusText,
},
Expand Down
20 changes: 18 additions & 2 deletions packages/better-fetch/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,27 @@ export type SuccessContext<Res = any> = {
response: Response;
request: RequestContext;
};
export type ErrorContext = {
export type ValidationErrorContext = {
response: Response;
request: RequestContext;
error: BetterFetchError & Record<string, any>;
error: {
type: 'validation';
issues: ReadonlyArray<StandardSchemaV1.Issue>;
message: string;
status?: number;
statusText?: string;
};
};

export type HttpErrorContext = {
response: Response;
request: RequestContext;
error: BetterFetchError & Record<string, any> & {
type: 'http';
};
};

export type ErrorContext = HttpErrorContext | ValidationErrorContext;
export interface FetchHooks<Res = any> {
/**
* a callback function that will be called when a
Expand Down
Loading