API

When you create a new Better Query instance, it provides you with an api object. This object exposes every endpoint that exists in your Better Query instance. And you can use this to interact with Better Query server side.

Any endpoint added to Better Query, whether from plugins or the core, will be accessible through the api object.

Calling API Endpoints on the Server

To call an API endpoint on the server, import your query instance and call the endpoint using the api object. The available endpoints are generated based on your resource configuration.

server.ts
import { betterQuery, createResource, withId } from "better-query";
import { z } from "zod";
import { headers } from "next/headers";

// Define your resources first
const userSchema = withId({
    name: z.string(),
    email: z.string().email(),
    role: z.string().default("user")
});

export const query = betterQuery({
    database: /* your database config */,
    resources: [
        createResource({
            name: "user",
            schema: userSchema,
        })
    ]
})

// Now you can call the generated endpoints
// calling list resources on the server
await query.api.listUsers({
    headers: await headers() // some endpoints might require headers
})

Body, Headers, Query

Unlike the client, the server needs the values to be passed as an object with the key body for the body, headers for the headers, and query for query parameters.

server.ts
// List users (GET endpoint)
await query.api.listUsers({
    headers: await headers()
})

// Create user (POST endpoint)
await query.api.createUser({
    body: {
        name: "John Doe",
        email: "john@doe.com",
        role: "user"
    },
    headers: await headers() // optional but would be useful to get the user IP, user agent, etc.
})

// Get single user (GET endpoint with ID parameter)
await query.api.getUser({
    query: {
        id: "user_123"
    }
})

Better Query API endpoints are built on top of better-call, a tiny web framework that lets you call REST API endpoints as if they were regular functions and allows us to easily infer client types from the server.

Getting headers and Response Object

When you invoke an API endpoint on the server, it will return a standard JavaScript object or array directly as it's just a regular function call.

But there are times when you might want to get the headers or the Response object instead. For example, if you need to get the cookies or the headers.

Getting headers

To get the headers, you can pass the returnHeaders option to the endpoint.

const { headers, response } = await query.api.createUser({
	returnHeaders: true,
	body: {
		name: "John Doe",
		email: "john@doe.com",
		role: "user",
	},
});

The headers will be a Headers object, which you can use to get the cookies or the headers.

const cookies = headers.get("set-cookie");
const customHeader = headers.get("x-custom-header");

Getting Response Object

To get the Response object, you can pass the asResponse option to the endpoint.

server.ts
const response = await query.api.createUser({
    body: {
        name: "John Doe",
        email: "john@doe.com"
    },
    asResponse: true
})

Error Handling

When you call an API endpoint on the server, it will throw an error if the request fails. You can catch the error and handle it as you see fit. The error instance is an instance of APIError.

server.ts
import { APIError } from "better-call";

try {
    await query.api.createUser({
        body: {
            name: "",
            email: ""
        }
    })
} catch (error) {
    if (error instanceof APIError) {
        console.log(error.message, error.status)
    }
}

On this page