Better Fetch

Fetch Schema

Fetch schema allows you to pre-define the url path and the shape of reqeust and response data. You can easily document your api using this schema.

The output of the scheam will be validated using zod and if the validation fails, it'll throw an error.

Before we start, make sure you have installed the zod package.

npm i zod

to create a fetch schema, you need to import the createSchema function from @better-fetch/fetch.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
 
export const  = ({ 
    "/path": { 
        : .({ 
            : .(), 
            : .(), 
            : .(), 
            : .(), 
        }), 
        : .({ 
            : .(), 
            : .(), 
            : .(), 
            : .(), 
        }), 
    } 
}) 
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    :  
});

Fetch Schema

The Fetch Schema is a map of path/url and schema. The path is the url path and the schema is an object with input, output, query and params keys.

The input key is the schema of the request data. The output key is the schema of the response data. The query key is the schema of the query params. The params key is dynamic path parameters.

Input

The input schema is the schema of the request data. The input key is the schema of the request data. If you defined an input schema, the data will be requeired to be passed as a body of the request.

If you define an input schema, a post method will be used to make the request and if there is no input schema, a get method will be used. See method modifiers section for defining specefic methods.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : ({
        "/path": {
            : .({
                : .(),
                : .(),
                : .(),
                : .(),
            }),
        },
    }), 
})
 
const { ,  } = await ("/path", {
    body: {}
Type '{}' is missing the following properties from type '{ userId: string; id: number; title: string; completed: boolean; }': userId, id, title, completed
})

To make the body optional you can wrap the schema with z.optional.

Output

The output schema is the schema of the response data. The output key is the schema of the response data. If you defined an output schema, the data will be returned as the response body.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : ({
        "/path": {
            : .({
                : .(),
                : .(),
                : .(),
                : .(),
            }),
        },
    }), 
})
 
const { ,  } = await ("/path")
 
Hover over the data object to see the type

Query

The query schema is the schema of the query params. The query key is the schema of the query params. If you defined a query schema, the data will be passed as the query params.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : ({
        "/path": {
            : .({
                : .(),
                : .(),
                : .(),
                : .(),
            }),
        },
    }), 
})
 
const { ,  } = await ("/path", {  
    query: {}
Type '{}' is missing the following properties from type '{ userId: string; id: number; title: string; completed: boolean; }': userId, id, title, completed
})
Hover over the data object to see the type

Dynamic Path Parameters

The params schema is the schema of the path params. You can either use the params key to define the paramters or prepend : to the path to make the parameters dynamic.

If you define more than one dynamic path parameter using the string modifier the paramters will be required to be passed as an array of values in the order they are defined.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";    
 
const  = ({
    "/user/:id": {
        : .({
            : .(),
        }),
    },
    "/post": {
        : .({
            : .(),
            : .(),
        }),
    },
    "/post/:id/:title": {
        : .({
            : .(),
        }),
    }
}) 
 
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : 
})
 
const  = await ("/user/:id", {
    : {
        : "1",
    }
})
 
const  = await ("/post", {
    : {
        : "1",
        : "title"
    },
})
 
const  = await ("/post/:id/:title", {
    : {
        : "1",
        : "title"
    }
})

Method Modifiers

By default the get and post methods are used to make the request based on weather the input schema is defined or not. You can use the method modifier to define the method to be used.

The method modifiers are @get, @post, @put, @patch, @delete and @head. You prepend the method name to the path to define the method

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : ({
        "@put/user": { 
            : .({
                : .(),
                : .(),
            }),
            : .({
               : .(),
               : .(),
            }),
        },
    }), 
})
 
const { ,  } = await ("/@put/user", {
    : {
        : "title",
        : true,
    }
})
 
the request will be made to "/user" path with a PUT method.

Strict Schema

By default if you define schema better fetch still allows you to make a call to other routes that's not defined on the schema. If you want to enforce only the keys defined to be inferred as valid you can use pass the strict option to the schema.

fetch.ts
import { ,  } from "@better-fetch/fetch";
import {  } from "zod";
 
const  = ({
    : "https://jsonplaceholder.typicode.com",
    : ({
        "/path": {
            : .({
                : .(),
                : .(),
                : .(),
                : .(),
            }),
        },
    }, 
    { 
        : true
    }), 
})
const { ,  } = await ("/invalid-path")
Argument of type '"/invalid-path"' is not assignable to parameter of type '"/path"'.

On this page